<commit_before>from hashlib import sha256, new
from binascii import hexlify, unhexlify
from base58 import b58encode, b58decode
from secp256k1 import PrivateKey
import networks

class Kutil:
    '''pubkey/privkey operations'''

    def __init__(self, wif=None, privkey=None, network=None, seed=None):
        '''wif=<WIF> import private key from your wallet in WIF format
           privkey=<privkey> import private key in binary format
           network=<network> specify network [ppc, tppc, btc]
           seed=<seed> specify seed (string) to make the privkey from'''

        if wif is not None:
            key = self.wif_to_privkey(wif)
            self.keypair = PrivateKey(key['privkey'])
            network = key['net_prefix']

        if seed is not None:
            self.keypair = PrivateKey(raw=bytes([seed]))

        if privkey is not None:
            self.keypair = PrivateKey(unhexlify(privkey))

        if (privkey and wif and seed) is None:
            self.keypair = PrivateKey() # new keypair

        if network is None:
            self.load_network_parameters('ppc')
        else:
            self.load_network_parameters(network)

        self._privkey = self.keypair.private_key
        self._pubkey = self.keypair.pubkey.serialize(compressed=False)
        self._pubkey_compressed = self.keypair.pubkey.serialize(compressed=True)

    def load_network_parameters(self, query):
        '''loads network parameters and sets class variables'''

        (self._network_name, self._network_shortname, self._subnet_name,
         self._pubkeyhash, self._wif_prefix, self._scripthash,
         self._magicbytes) = tuple(networks.query(query))

    @property
    def privkey(self):
        '''retrun privkey in hex format'''

        return hexlify(self._privkey).decode("utf-8")

    @property
    def pubkey(self):
        '''return compressed pubkey in hex format'''

        return hexlify(self._pubkey_compressed).decode("utf-8")

    @property
    def address(self):
        '''generate an address from pubkey'''

        keyhash = unhexlify(self._pubkeyhash + hexlify(
            new('ripemd160', sha256(self._pubkey).digest()).
            digest())
                           )

        checksum = sha256(
            sha256(keyhash).digest()
            ).hexdigest()[0:8]

        return b58encode(keyhash + unhexlify(checksum))

    @staticmethod
    def check_wif(wif):
        '''check if WIF is properly formated.'''

        b58_wif = b58decode(wif)
        check = b58_wif[-4:]
        checksum = sha256(sha256(b58_wif[:-4]).digest()).digest()[0:4]
        return checksum == check

    def to_wif(self, compressed=False):
        '''convert raw private key to WIF'''

        extkey = self._wif_prefix + self.privkey.encode("utf-8")
        if compressed:
            extkey += b'01'

        extcheck = unhexlify(extkey) + sha256(sha256(unhexlify(extkey
                                                              )).digest()).digest()[0:4]
        wif = b58encode(extcheck)

        assert self.check_wif(wif)
        return wif

    def wif_to_privkey(self, wif):
        '''import WIF'''

        assert self.check_wif(wif)
        b58_wif = b58decode(wif)

        if len(wif) == 51:
            return {'privkey': b58_wif[1:-4], 'net_prefix': hexlify(b58_wif[0:1])}
        if len(wif) == 52:
            return {'privkey': b58_wif[1:-5], 'net_prefix': hexlify(b58_wif[0:1])}
        else:
            return 'Invalid WIF length'

    def sign(self, message):
        '''sing >message< with the privkey'''

        return self.keypair.ecdsa_sign(message.encode('utf-8'))

    def verify(self, message, signature):
        '''verify >message< against >signature<'''

        return self.keypair.pubkey.ecdsa_verify(message.encode('utf-8'), signature)
<commit_msg>added support for address made from compressed pubkey<commit_after>from hashlib import sha256, new
from binascii import hexlify, unhexlify
from base58 import b58encode, b58decode
from secp256k1 import PrivateKey
import networks

class Kutil:
    '''pubkey/privkey operations'''

    def __init__(self, wif=None, privkey=None, network=None, seed=None):
        '''wif=<WIF> import private key from your wallet in WIF format
           privkey=<privkey> import private key in binary format
           network=<network> specify network [ppc, tppc, btc]
           seed=<seed> specify seed (string) to make the privkey from'''

        if wif is not None:
            key = self.wif_to_privkey(wif)
            self.keypair = PrivateKey(key['privkey'])
            network = key['net_prefix']

        if seed is not None:
            self.keypair = PrivateKey(raw=bytes([seed]))

        if privkey is not None:
            self.keypair = PrivateKey(unhexlify(privkey))

        if (privkey and wif and seed) is None:
            self.keypair = PrivateKey() # new keypair

        if network is None:
            self.load_network_parameters('ppc')
        else:
            self.load_network_parameters(network)

        self._privkey = self.keypair.private_key
        self._pubkey = self.keypair.pubkey.serialize(compressed=False)
        self._pubkey_compressed = self.keypair.pubkey.serialize(compressed=True)

    def load_network_parameters(self, query):
        '''loads network parameters and sets class variables'''

        (self._network_name, self._network_shortname, self._subnet_name,
         self._pubkeyhash, self._wif_prefix, self._scripthash,
         self._magicbytes) = tuple(networks.query(query))

    @property
    def privkey(self):
        '''retrun privkey in hex format'''

        return hexlify(self._privkey).decode("utf-8")

    @property
    def pubkey(self):
        '''return compressed pubkey in hex format'''

        return hexlify(self._pubkey_compressed).decode("utf-8")

    def address(self, compressed=False):
        '''generate an address from pubkey'''

        if not compressed:
            keyhash = unhexlify(mykey._pubkeyhash + hexlify(
                new('ripemd160', sha256(mykey._pubkey).digest()).
                digest())
                               )
        else:
            keyhash = unhexlify(mykey._pubkeyhash + hexlify(
                new('ripemd160', sha256(mykey._pubkey_compressed + b'01').digest()).
                digest())
                               )

        checksum = sha256(
            sha256(keyhash).digest()
            ).hexdigest()[0:8]

        return b58encode(keyhash + unhexlify(checksum))

    @staticmethod
    def check_wif(wif):
        '''check if WIF is properly formated.'''

        b58_wif = b58decode(wif)
        check = b58_wif[-4:]
        checksum = sha256(sha256(b58_wif[:-4]).digest()).digest()[0:4]
        return checksum == check

    def to_wif(self, compressed=False):
        '''convert raw private key to WIF'''

        extkey = self._wif_prefix + hexlify(self._privkey)
        if compressed:
            extkey += b'01'

        extcheck = unhexlify(extkey) + sha256(sha256(unhexlify(extkey
                                                              )).digest()).digest()[0:4]
        wif = b58encode(extcheck)

        assert self.check_wif(wif)
        return wif

    def wif_to_privkey(self, wif):
        '''import WIF'''

        assert self.check_wif(wif)
        b58_wif = b58decode(wif)

        if len(wif) == 51:
            return {'privkey': b58_wif[1:-4], 'net_prefix': hexlify(b58_wif[0:1])}
        if len(wif) == 52:
            return {'privkey': b58_wif[1:-5], 'net_prefix': hexlify(b58_wif[0:1])}
        else:
            return 'Invalid WIF length'

    def sign(self, message):
        '''sing >message< with the privkey'''

        return self.keypair.ecdsa_sign(message.encode('utf-8'))

    def verify(self, message, signature):
        '''verify >message< against >signature<'''

        return self.keypair.pubkey.ecdsa_verify(message.encode('utf-8'), signature)
<|endoftext|>


<commit_before>from __future__ import print_function

__version__ = '1.84'
import sys

if sys.version[0] == '3':
    import io as StringIO  # Python 3.x
else:
    import cStringIO as StringIO  # Python 2.x

    range = xrange

from pyperform.benchmark import Benchmark
from .comparisonbenchmark import ComparisonBenchmark
from .benchmarkedclass import BenchmarkedClass
from .benchmarkedfunction import BenchmarkedFunction
from .thread import Thread
from .timer import timer
from .exceptions import ValidationError
from .customlogger import CustomLogLevel, new_log_level

def enable():
    """
    Enable all benchmarking.
    """
    Benchmark.enable = True
    ComparisonBenchmark.enable = True
    BenchmarkedFunction.enable = True
    BenchmarkedClass.enable = True

def disable():
    """
    Disable all benchmarking.
    """
    Benchmark.enable = False
    ComparisonBenchmark.enable = False
    BenchmarkedFunction.enable = False
    BenchmarkedClass.enable = False

<commit_msg>bump to 1.85<commit_after>from __future__ import print_function

__version__ = '1.85'
import sys

if sys.version[0] == '3':
    import io as StringIO  # Python 3.x
else:
    import cStringIO as StringIO  # Python 2.x

    range = xrange

from pyperform.benchmark import Benchmark
from .comparisonbenchmark import ComparisonBenchmark
from .benchmarkedclass import BenchmarkedClass
from .benchmarkedfunction import BenchmarkedFunction
from .thread import Thread
from .timer import timer
from .exceptions import ValidationError
from .customlogger import CustomLogLevel, new_log_level

def enable():
    """
    Enable all benchmarking.
    """
    Benchmark.enable = True
    ComparisonBenchmark.enable = True
    BenchmarkedFunction.enable = True
    BenchmarkedClass.enable = True

def disable():
    """
    Disable all benchmarking.
    """
    Benchmark.enable = False
    ComparisonBenchmark.enable = False
    BenchmarkedFunction.enable = False
    BenchmarkedClass.enable = False

<|endoftext|>


<commit_before>from pyramid.httpexceptions import HTTPFound
import venusian
import six
import logging
from functools import partial
from .util import get_pks
from .forms import ButtonForm
from sqlalchemy import inspect
try:
    from collections import OrderedDict
except:
    from ordereddict import OrderedDict

log = logging.getLogger(__name__)

class CRUDCreator(type):
    """
    Metaclass for :class:`CRUDView` to handle automatically registering views
    for new subclasses.
    """

    def __init__(cls, name, bases, attrs):
        def cb(context, name, ob):
            config = context.config.with_package(info.module)
            config.add_route(cls.route_name('list'),
                             '%s' % cls.url_path)
            config.add_view(cls, attr='list',
                            route_name=cls.route_name('list'),
                            renderer=cls._template_for('list'),
                            request_method='GET')

            config.add_route(cls.route_name('new'),
                             '%s/new' % cls.url_path)
            config.add_view(cls, attr='edit', route_name=cls.route_name('new'),
                            renderer=cls._template_for('edit'))
            pks = ",".join('{%s}' % pk_name
                           for pk_name in get_pks(cls.form.Meta.model))
            config.add_route(cls.route_name('edit'),
                             '%s/%s' % (cls.url_path, pks))
            config.add_view(cls, attr='edit',
                            route_name=cls.route_name('edit'),
                            renderer=cls._template_for('edit'))

            config.add_route(cls.route_name('delete'),
                             '%s/delete/%s' % (cls.url_path, pks))
            config.add_view(cls, attr='delete',
                            route_name=cls.route_name('delete'),
                            request_method='POST')
        if '__abstract__' not in attrs:
            have_attrs = set(attrs)
            need_attrs = {'Form', 'url_path'}
            if have_attrs & need_attrs != need_attrs:
                missing = need_attrs - (have_attrs & need_attrs)
                raise AttributeError(
                    "Invalid configuration. The following attributes are "
                    "missing and need to be defined for a complete "
                    "configuration : %s" % ", ".join(missing))
            info = venusian.attach(cls, cb)

@six.add_metaclass(CRUDCreator)
class CRUDView(object):
    """
    The base class for all views. Views directly or indirectly subclassed from
    this define actual views (or intermediate base classes, if ``__abstract__``
    is ``True``).

    The following attributes can be defined to override behavior of the view:

    ``Form``: Mandatory argument that specifies the form class for which this
    view should be created. This must be a form as described in :ref:`forms`.

    ``url_path``: Mandatory argument for defining the base path under which
    this view should be available.

    ``dbsession``: Return the current SQLAlchemy session. By default this
    expects a ``dbsession`` attribute on the ``request`` object. It is
    **mandatory** that you either attach the attribute using an event
    or override this attribute (you can use a :class:`property` if you
    like).

    ``title``: The title of the view. By default it uses the name of the model
    class.

    ``title_plural``: The title when used in plural. By default this attached
    an "s" to the ``title``.

    ``template_dir``: The directory where to find templates. The default
    templates are provided in the ``default`` folder.

    ``template_ext``: Which file extension to use for templates. By default,
    Mako templates are used and so the extension is ``.mako`` but any
    rendered that is recognized by pramid can be used.

    ``template_base_name``: The name of the base template, i.e. from which all
    templates should inherit. Using this you can override the general style
    of the page using a single template instead of having to copy all of
    them and editing them one-by-one. It defaults to ``base``.

    ``button_form``: The form which to use for button actions that should be
    protected by CSRF. Normally, this does not need to be overridden,
    except if a change in those button-only forms such as the delete button
    in the list view is desired. Contrary to a Link this is a necessity to
    prevent CSRF attacks.

    ``delete_form_factory``: A callable which creates a delete form suitable
    for being displayed as a button to delete an item. By default this is
    based on the ``button_form`` attribute and additionally to all
    typical arguments being accepted, it sets the button value to
    ``Delete``.
    """
    __abstract__ = True
    template_dir = 'default'
    template_ext = '.mako'
    template_base_name = 'base'
    button_form = ButtonForm
    delete_form_factory = partial(ButtonForm, title='Delete')

    def __init__(self, request):
        self.request = request
        self._delete_form = None

    def delete_form(self):
        """
        Get the delete form instance, creating a new one if there is none yet.
        This will always return the same instance after multiple calls. This
        shouldn't lead to problems even when the same instance is used for
        multiple elements (like in a list display) since only a button and
        the CSRF hidden field exist on it.
        """
        if self._delete_form is None:
            self._delete_form = self.delete_form_factory(
                self.request.POST, csrf_context=self.request)
        return self._delete_form

    @property
    def dbsession(self):
        return self.request.dbsession

    @property
    def title(self):
        return inspect(self.Form.Meta.model).class_.__name__

    @property
    def title_plural(self):
        return self.title + "s"

    # Misc helper stuff

    @classmethod
    def _template_for(cls, action):
        """
        Return the name of the template to be used. By default this uses the
        template in the folder ``template_dir`` with the name
        ``action + template_ext``, so for example in the default case for a
        list view: "default/list.mako"
        """
        default_name = '%s/%s%s' % (cls.template_dir, action, cls.template_ext)
        return getattr(cls, '%s_template' % action, default_name)

    # Routing stuff

    def redirect(self, route_name=None, *args, **kw):
        """
        Convenience function to create a redirect.

        :param route_name: The name of the route for which to create a URL.
            If this is ``None``, the current route is used.

        All additional arguments and keyword arguments are passed to
        :meth:`pyramid.request.Request.route_url`.

        :return: An instance of :exc:`pyramid.httpexceptions.HTTPFound`
            suitable to be returned from a view to create a redirect.
        """
        if route_name is None:
            route_name = self.request.matched_route.name
        return HTTPFound(
            location=self.request.route_url(route_name, *args, **kw)
        )

    @classmethod
    def route_name(cls, action):
        """
        Get a name for a route of a specific action. The default implementation
        provides the fully quallyfied name of the view plus the action, e.g.
        ``mypackage.views.MyView.list``.
        """
        params = {'module': cls.__module__,
                  'class': cls.__name__,
                  'action': action}
        return "%(module)s.%(class)s.%(action)s" % params

    def _get_route_pks(self, obj):
        """
        Get a dictionary mapping primary key names to values based on the model
        (contrary to :meth:`_get_request_pks` which bases them on the
        request).

        :param obj: An instance of the model.

        :return: A dict with primary key names as keys and their values on the
            object instance as the values.
        """
        Model = self.Form.Meta.model
        pk_names = get_pks(Model)
        kw = {}
        for pk in pk_names:
            kw[pk] = getattr(obj, pk)
            if kw[pk] is None:
                raise ValueError("An obj needs to have all primary keys "
                                 "set or no route can be generated")
        return kw

    def _edit_route(self, obj):
        """
        Get a route for the edit action. Behaves the same as
        :meth:`_delete_route`.
        """
        kw = self._get_route_pks(obj)
        return self.request.route_url(self.route_name('edit'), **kw)

    def _delete_route(self, obj):
        """
        Get a route for the delete action based on the primary keys.

        :param obj: The instance of a model on which the routes values should
            be based.

        :return: A URL which can be used as the routing URL for redirects or
            displaying the URL on the page.
        """
        kw = self._get_route_pks(obj)
        return self.request.route_url(self.route_name('delete'), **kw)

    def _get_request_pks(self):
        """
        Get an ordered dictionary of primary key names matching to their value,
        fetched from the request's matchdict (not the model!).

        :param names: An iterable of names which are to be fetched from the
            matchdict.

        :return: An :class:`.OrderedDict` of the given ``names`` as keys with
            their corresponding value.

        :raises ValueError: When only some primary keys are set (it is allowed
            to have all or none of them set)
        """
        data = OrderedDict((key, self.request.matchdict.get(key, None))
                           for key in get_pks(self.Form.Meta.model))
        nones = [val is None for val in data.values()]
        if any(nones):
            if not all(nones):
                raise ValueError("Either all primary keys have to be set or "
                                 "None")
            else:
                return None
        else:
            return data

    # Actual admin views

    def list(self):
        """
        List all items for a Model. This is the default view that can be
        overridden by subclasses to change its behavior. The attached default
        template is named "default/list.mako".

        :return: A dict with a single key ``items`` that is a query which when
            iterating over yields all items to be listed.
        """
        query = self.dbsession.query(self.Form.Meta.model)
        return {'items': query}

    def delete(self):
        """
        Delete an item. This is the default method for handling deletion and
        can be overridden by subclasses. It only accepts POST request. It has
        no template attached and instead always returns a redirect.

        :return: An instance of :class:`.HTTPFound` to redirect the user,
            either deletion was successful or an error has happened.
        """
        assert self.request.method == 'POST'

        redirect = self.redirect(self.route_name('list'))
        Model = self.Form.Meta.model
        try:
            pks = self._get_request_pks()
            if pks is None:
                raise ValueError("Need primary keys for deletion")
        except ValueError as exc:
            log.info("Invalid Request for primary keys: %s threw exception %s"
                     % (self.request.matchdict, exc))
            self.request.session.flash("Invalid URL", 'error')
            raise redirect

        if not self.delete_form().validate():
            self.request.session.flash("Delete failed.", 'error')
            raise redirect

        obj = self.dbsession.query(Model).get(tuple(pks.values()))
        if obj is not None:
            self.dbsession.delete(obj)
            self.request.session.flash("%s deleted!" % self.title)
            return redirect
        else:
            self.request.session.flash("%s not found!" % self.title, 'error')
            raise redirect

    def edit(self):
        """
        The default view for editing an item. It loads the configured form and
        model. The default template is "default/edit.mako".

        :return: In case of a GET request a dict with the key ``form`` denoting
            the configured form instance with data from an optional model
            loaded and a key ``is_new`` which is a boolean flag indicating
            whether the actual action is ``new`` or ``edit`` (allowing for
            templates to display "New Item" or "Edit Item").

            In case of a POST request, either the same dict is returned or an
            instance of :class:`.HTTPFound` which indicates success in saving
            the item to the database.

        :raises ValueError: In case of an invalid, missing or unmatched action.
            The most likely reason for this is the missing button of a form,
            e.g. by the name ``save``. By default the following actions are
            supported: ``save``, ``save_close``, ``save_new`` and additionally
            anything that starts with ``add_`` or ``delete_`` (these two are
            for internal form handling and inline deletes/adds).
        """
        Model = self.Form.Meta.model

        # determine primary keys
        try:
            pks = self._get_request_pks()
        except ValueError as exc:
            log.info("Invalid Request for primary keys: %s threw exception %s"
                     % (self.request.matchdict, exc))
            self.request.session.flash("Invalid URL")
            raise self.redirect(self.route_name('list'))

        if pks is not None:
            is_new = True
            obj = self.dbsession.query(Model).get(tuple(pks.values()))
            form = self.Form(self.request.POST, obj, csrf_context=self.request)
        else:
            is_new = False
            form = self.Form(self.request.POST, csrf_context=self.request)
        form.session = self.request.dbsession

        # Prepare return values
        retparams = {'form': form, 'is_new': is_new}

        if self.request.method == 'POST':
            # TODO: Cancel, Save & New, Save & Close, Save
            actions = ['save', 'save_close', 'save_new']
            for action in actions:
                if action in self.request.POST:
                    break
            else:
                for key in self.request.POST:
                    # increase the extra choices by one
                    if key.startswith("add_") or key.startswith("delete_"):
                        return retparams
                raise ValueError("Unmatched/Missing Action")

            if not form.validate():
                return retparams

            # New object or existing one?
            # Here we do stuff specific to the is_new state, followed by
            # general operations
            if is_new:
                obj = Model()
                self.dbsession.add(obj)
                self.request.session.flash("%s added!" % self.title)
            else:
                self.request.session.flash("%s edited!" % self.title)

            # Transfer edits into database
            form.populate_obj(obj)

            # Determine redirect
            if action == 'save':
                self.dbsession.flush()
                return HTTPFound(location=self._edit_route(obj))
            elif action == 'save_close':
                return self.redirect(self.route_name('list'))
            elif action == 'save_new':
                return self.redirect(self.route_name('new'))
            else:
                raise ValueError("Unmatched action")
        else:
            return retparams
<commit_msg>Fix set expression in python 2.6 (not supported)<commit_after>from pyramid.httpexceptions import HTTPFound
import venusian
import six
import logging
from functools import partial
from .util import get_pks
from .forms import ButtonForm
from sqlalchemy import inspect
try:
    from collections import OrderedDict
except:
    from ordereddict import OrderedDict

log = logging.getLogger(__name__)

class CRUDCreator(type):
    """
    Metaclass for :class:`CRUDView` to handle automatically registering views
    for new subclasses.
    """

    def __init__(cls, name, bases, attrs):
        def cb(context, name, ob):
            config = context.config.with_package(info.module)
            config.add_route(cls.route_name('list'),
                             '%s' % cls.url_path)
            config.add_view(cls, attr='list',
                            route_name=cls.route_name('list'),
                            renderer=cls._template_for('list'),
                            request_method='GET')

            config.add_route(cls.route_name('new'),
                             '%s/new' % cls.url_path)
            config.add_view(cls, attr='edit', route_name=cls.route_name('new'),
                            renderer=cls._template_for('edit'))
            pks = ",".join('{%s}' % pk_name
                           for pk_name in get_pks(cls.form.Meta.model))
            config.add_route(cls.route_name('edit'),
                             '%s/%s' % (cls.url_path, pks))
            config.add_view(cls, attr='edit',
                            route_name=cls.route_name('edit'),
                            renderer=cls._template_for('edit'))

            config.add_route(cls.route_name('delete'),
                             '%s/delete/%s' % (cls.url_path, pks))
            config.add_view(cls, attr='delete',
                            route_name=cls.route_name('delete'),
                            request_method='POST')
        if '__abstract__' not in attrs:
            have_attrs = set(attrs)
            need_attrs = set(('Form', 'url_path'))
            if have_attrs & need_attrs != need_attrs:
                missing = need_attrs - (have_attrs & need_attrs)
                raise AttributeError(
                    "Invalid configuration. The following attributes are "
                    "missing and need to be defined for a complete "
                    "configuration : %s" % ", ".join(missing))
            info = venusian.attach(cls, cb)

@six.add_metaclass(CRUDCreator)
class CRUDView(object):
    """
    The base class for all views. Views directly or indirectly subclassed from
    this define actual views (or intermediate base classes, if ``__abstract__``
    is ``True``).

    The following attributes can be defined to override behavior of the view:

    ``Form``: Mandatory argument that specifies the form class for which this
    view should be created. This must be a form as described in :ref:`forms`.

    ``url_path``: Mandatory argument for defining the base path under which
    this view should be available.

    ``dbsession``: Return the current SQLAlchemy session. By default this
    expects a ``dbsession`` attribute on the ``request`` object. It is
    **mandatory** that you either attach the attribute using an event
    or override this attribute (you can use a :class:`property` if you
    like).

    ``title``: The title of the view. By default it uses the name of the model
    class.

    ``title_plural``: The title when used in plural. By default this attached
    an "s" to the ``title``.

    ``template_dir``: The directory where to find templates. The default
    templates are provided in the ``default`` folder.

    ``template_ext``: Which file extension to use for templates. By default,
    Mako templates are used and so the extension is ``.mako`` but any
    rendered that is recognized by pramid can be used.

    ``template_base_name``: The name of the base template, i.e. from which all
    templates should inherit. Using this you can override the general style
    of the page using a single template instead of having to copy all of
    them and editing them one-by-one. It defaults to ``base``.

    ``button_form``: The form which to use for button actions that should be
    protected by CSRF. Normally, this does not need to be overridden,
    except if a change in those button-only forms such as the delete button
    in the list view is desired. Contrary to a Link this is a necessity to
    prevent CSRF attacks.

    ``delete_form_factory``: A callable which creates a delete form suitable
    for being displayed as a button to delete an item. By default this is
    based on the ``button_form`` attribute and additionally to all
    typical arguments being accepted, it sets the button value to
    ``Delete``.
    """
    __abstract__ = True
    template_dir = 'default'
    template_ext = '.mako'
    template_base_name = 'base'
    button_form = ButtonForm
    delete_form_factory = partial(ButtonForm, title='Delete')

    def __init__(self, request):
        self.request = request
        self._delete_form = None

    def delete_form(self):
        """
        Get the delete form instance, creating a new one if there is none yet.
        This will always return the same instance after multiple calls. This
        shouldn't lead to problems even when the same instance is used for
        multiple elements (like in a list display) since only a button and
        the CSRF hidden field exist on it.
        """
        if self._delete_form is None:
            self._delete_form = self.delete_form_factory(
                self.request.POST, csrf_context=self.request)
        return self._delete_form

    @property
    def dbsession(self):
        return self.request.dbsession

    @property
    def title(self):
        return inspect(self.Form.Meta.model).class_.__name__

    @property
    def title_plural(self):
        return self.title + "s"

    # Misc helper stuff

    @classmethod
    def _template_for(cls, action):
        """
        Return the name of the template to be used. By default this uses the
        template in the folder ``template_dir`` with the name
        ``action + template_ext``, so for example in the default case for a
        list view: "default/list.mako"
        """
        default_name = '%s/%s%s' % (cls.template_dir, action, cls.template_ext)
        return getattr(cls, '%s_template' % action, default_name)

    # Routing stuff

    def redirect(self, route_name=None, *args, **kw):
        """
        Convenience function to create a redirect.

        :param route_name: The name of the route for which to create a URL.
            If this is ``None``, the current route is used.

        All additional arguments and keyword arguments are passed to
        :meth:`pyramid.request.Request.route_url`.

        :return: An instance of :exc:`pyramid.httpexceptions.HTTPFound`
            suitable to be returned from a view to create a redirect.
        """
        if route_name is None:
            route_name = self.request.matched_route.name
        return HTTPFound(
            location=self.request.route_url(route_name, *args, **kw)
        )

    @classmethod
    def route_name(cls, action):
        """
        Get a name for a route of a specific action. The default implementation
        provides the fully quallyfied name of the view plus the action, e.g.
        ``mypackage.views.MyView.list``.
        """
        params = {'module': cls.__module__,
                  'class': cls.__name__,
                  'action': action}
        return "%(module)s.%(class)s.%(action)s" % params

    def _get_route_pks(self, obj):
        """
        Get a dictionary mapping primary key names to values based on the model
        (contrary to :meth:`_get_request_pks` which bases them on the
        request).

        :param obj: An instance of the model.

        :return: A dict with primary key names as keys and their values on the
            object instance as the values.
        """
        Model = self.Form.Meta.model
        pk_names = get_pks(Model)
        kw = {}
        for pk in pk_names:
            kw[pk] = getattr(obj, pk)
            if kw[pk] is None:
                raise ValueError("An obj needs to have all primary keys "
                                 "set or no route can be generated")
        return kw

    def _edit_route(self, obj):
        """
        Get a route for the edit action. Behaves the same as
        :meth:`_delete_route`.
        """
        kw = self._get_route_pks(obj)
        return self.request.route_url(self.route_name('edit'), **kw)

    def _delete_route(self, obj):
        """
        Get a route for the delete action based on the primary keys.

        :param obj: The instance of a model on which the routes values should
            be based.

        :return: A URL which can be used as the routing URL for redirects or
            displaying the URL on the page.
        """
        kw = self._get_route_pks(obj)
        return self.request.route_url(self.route_name('delete'), **kw)

    def _get_request_pks(self):
        """
        Get an ordered dictionary of primary key names matching to their value,
        fetched from the request's matchdict (not the model!).

        :param names: An iterable of names which are to be fetched from the
            matchdict.

        :return: An :class:`.OrderedDict` of the given ``names`` as keys with
            their corresponding value.

        :raises ValueError: When only some primary keys are set (it is allowed
            to have all or none of them set)
        """
        data = OrderedDict((key, self.request.matchdict.get(key, None))
                           for key in get_pks(self.Form.Meta.model))
        nones = [val is None for val in data.values()]
        if any(nones):
            if not all(nones):
                raise ValueError("Either all primary keys have to be set or "
                                 "None")
            else:
                return None
        else:
            return data

    # Actual admin views

    def list(self):
        """
        List all items for a Model. This is the default view that can be
        overridden by subclasses to change its behavior. The attached default
        template is named "default/list.mako".

        :return: A dict with a single key ``items`` that is a query which when
            iterating over yields all items to be listed.
        """
        query = self.dbsession.query(self.Form.Meta.model)
        return {'items': query}

    def delete(self):
        """
        Delete an item. This is the default method for handling deletion and
        can be overridden by subclasses. It only accepts POST request. It has
        no template attached and instead always returns a redirect.

        :return: An instance of :class:`.HTTPFound` to redirect the user,
            either deletion was successful or an error has happened.
        """
        assert self.request.method == 'POST'

        redirect = self.redirect(self.route_name('list'))
        Model = self.Form.Meta.model
        try:
            pks = self._get_request_pks()
            if pks is None:
                raise ValueError("Need primary keys for deletion")
        except ValueError as exc:
            log.info("Invalid Request for primary keys: %s threw exception %s"
                     % (self.request.matchdict, exc))
            self.request.session.flash("Invalid URL", 'error')
            raise redirect

        if not self.delete_form().validate():
            self.request.session.flash("Delete failed.", 'error')
            raise redirect

        obj = self.dbsession.query(Model).get(tuple(pks.values()))
        if obj is not None:
            self.dbsession.delete(obj)
            self.request.session.flash("%s deleted!" % self.title)
            return redirect
        else:
            self.request.session.flash("%s not found!" % self.title, 'error')
            raise redirect

    def edit(self):
        """
        The default view for editing an item. It loads the configured form and
        model. The default template is "default/edit.mako".

        :return: In case of a GET request a dict with the key ``form`` denoting
            the configured form instance with data from an optional model
            loaded and a key ``is_new`` which is a boolean flag indicating
            whether the actual action is ``new`` or ``edit`` (allowing for
            templates to display "New Item" or "Edit Item").

            In case of a POST request, either the same dict is returned or an
            instance of :class:`.HTTPFound` which indicates success in saving
            the item to the database.

        :raises ValueError: In case of an invalid, missing or unmatched action.
            The most likely reason for this is the missing button of a form,
            e.g. by the name ``save``. By default the following actions are
            supported: ``save``, ``save_close``, ``save_new`` and additionally
            anything that starts with ``add_`` or ``delete_`` (these two are
            for internal form handling and inline deletes/adds).
        """
        Model = self.Form.Meta.model

        # determine primary keys
        try:
            pks = self._get_request_pks()
        except ValueError as exc:
            log.info("Invalid Request for primary keys: %s threw exception %s"
                     % (self.request.matchdict, exc))
            self.request.session.flash("Invalid URL")
            raise self.redirect(self.route_name('list'))

        if pks is not None:
            is_new = True
            obj = self.dbsession.query(Model).get(tuple(pks.values()))
            form = self.Form(self.request.POST, obj, csrf_context=self.request)
        else:
            is_new = False
            form = self.Form(self.request.POST, csrf_context=self.request)
        form.session = self.request.dbsession

        # Prepare return values
        retparams = {'form': form, 'is_new': is_new}

        if self.request.method == 'POST':
            # TODO: Cancel, Save & New, Save & Close, Save
            actions = ['save', 'save_close', 'save_new']
            for action in actions:
                if action in self.request.POST:
                    break
            else:
                for key in self.request.POST:
                    # increase the extra choices by one
                    if key.startswith("add_") or key.startswith("delete_"):
                        return retparams
                raise ValueError("Unmatched/Missing Action")

            if not form.validate():
                return retparams

            # New object or existing one?
            # Here we do stuff specific to the is_new state, followed by
            # general operations
            if is_new:
                obj = Model()
                self.dbsession.add(obj)
                self.request.session.flash("%s added!" % self.title)
            else:
                self.request.session.flash("%s edited!" % self.title)

            # Transfer edits into database
            form.populate_obj(obj)

            # Determine redirect
            if action == 'save':
                self.dbsession.flush()
                return HTTPFound(location=self._edit_route(obj))
            elif action == 'save_close':
                return self.redirect(self.route_name('list'))
            elif action == 'save_new':
                return self.redirect(self.route_name('new'))
            else:
                raise ValueError("Unmatched action")
        else:
            return retparams
<|endoftext|>


<commit_before>import numpy as np
import itertools
from scipy.ndimage import filters

def smooth(array, box, phase=False):
    """
    Imitates IDL's smooth function. Can also (correctly) smooth interferometric phases with the phase=True keyword.
    """
    if np.iscomplexobj(array):
        return filters.uniform_filter(array.real, box) + 1j * filters.uniform_filter(array.imag, box)
    elif phase is True:
        return np.angle(smooth(np.exp(1j * array), box))
    else:
        return filters.uniform_filter(array.real, box)

def rebin(arr, *shape, **kwargs):
    """
    Imitates IDL's rebin function. Allows also phase rebining.

    :author: Andreas Reigber
    """

    phase = False  # combination of *args and fixed keywords
    if 'phase' in kwargs:
        phase = kwargs['phase']  # works only in in python 3!

    if len(shape) == 1:  # allows to pass shape as list/array or normal arguments
        if type(shape[0]) == int:
            shape = [shape[0]]
        else:
            shape = list(shape[0])

    oarr = arr.copy()
    oshap = oarr.shape
    for d in range(arr.ndim):
        n1 = shape[d]
        n2 = oshap[d]
        if n1 < n2:
            s = list(oarr.shape)
            s.insert(d + 1, n2 // n1)
            s[d] = n1
            if phase is True:
                oarr = np.angle(np.exp(1j * oarr.reshape(s)).mean(d + 1))
            else:
                oarr = oarr.reshape(s).mean(d + 1)
        elif n1 > n2:
            oarr = oarr.repeat(n1 // n2, axis=d)
        else:
            pass
    return oarr

def polyfit2d(x, y, z, order=3):

    ncols = (order + 1) ** 2
    g = np.zeros((x.size, ncols))
    ij = itertools.product(range(order + 1), range(order + 1))
    for k, (i, j) in enumerate(ij):
        g[:, k] = x ** i * y ** j
    m, _, _, _ = np.linalg.lstsq(g, z)
    return m

def polyval2d(x, y, m):
    # x = x * 1.0
    #y = y * 1.0
    order = int(np.sqrt(len(m))) - 1
    ij = itertools.product(range(order + 1), range(order + 1))
    z = np.zeros_like(x, dtype='float64')
    for a, (i, j) in zip(m, ij):
        z += a * x ** i * y ** j
    return z

def coreg(arr1, arr2, sub=False):
    """
    Returns the coregistration offset between two complex arrays

    :author: Andreas Reigber
    :param arr1: The master image as (2D np.ndarray)
    :type arr1: complex
    :param arr2: The slave image as (2D np.ndarray)
    :type arr2: complex
    :param sub=True: Calculate subpixel offset.
    :type arr2: boolean
    :returns: tuple containing slave image offset in y and x
    """
    s = arr1.shape
    out1 = np.fft.rfft2(abs(arr1)).astype('complex64')
    out2 = np.fft.rfft2(abs(arr2)).astype('complex64')
    out1 *= np.conj(out2)
    out2 = np.fft.irfft2(out1).astype('float32')
    pos = out2.argmax()
    xoff = pos % s[1]
    yoff = pos / s[1]
    if xoff >= s[1] // 2:
        xoff = xoff - s[1]
    if yoff >= s[0] // 2:
        yoff = yoff - s[0]
    if sub is True:  # explicit polynominal expansion of the peak
        ysub = 0.5 * (out2[yoff - 1, xoff] - out2[yoff + 1, xoff]) / (out2[yoff - 1, xoff] +
                                                                      out2[yoff + 1, xoff] - 2 * out2[yoff, xoff])
        xsub = 0.5 * (out2[yoff, xoff - 1] - out2[yoff, xoff + 1]) / (out2[yoff, xoff - 1] +
                                                                      out2[yoff, xoff + 1] - 2 * out2[yoff, xoff])
        yoff += np.round(ysub, 2)
        xoff += np.round(xsub, 2)
    return yoff.astype('float32'), xoff.astype('float32')
<commit_msg>Keep dtype in rebin operations<commit_after>import numpy as np
import itertools
from scipy.ndimage import filters

def smooth(array, box, phase=False):
    """
    Imitates IDL's smooth function. Can also (correctly) smooth interferometric phases with the phase=True keyword.
    """
    if np.iscomplexobj(array):
        return filters.uniform_filter(array.real, box) + 1j * filters.uniform_filter(array.imag, box)
    elif phase is True:
        return np.angle(smooth(np.exp(1j * array), box))
    else:
        return filters.uniform_filter(array.real, box)

def rebin(arr, *shape, **kwargs):
    """
    Imitates IDL's rebin function. Allows also phase rebining.

    :author: Andreas Reigber
    """

    phase = False  # combination of *args and fixed keywords
    if 'phase' in kwargs:
        phase = kwargs['phase']  # works only in in python 3!

    if len(shape) == 1:  # allows to pass shape as list/array or normal arguments
        if type(shape[0]) == int:
            shape = [shape[0]]
        else:
            shape = list(shape[0])

    oarr = arr.copy()
    oshap = oarr.shape
    for d in range(arr.ndim):
        n1 = shape[d]
        n2 = oshap[d]
        if n1 < n2:
            s = list(oarr.shape)
            s.insert(d + 1, n2 // n1)
            s[d] = n1
            if phase is True:
                oarr = np.angle(np.exp(1j * oarr.reshape(s)).mean(d + 1))
            else:
                oarr = oarr.reshape(s).mean(d + 1).astype(oarr.dtype)
        elif n1 > n2:
            oarr = oarr.repeat(n1 // n2, axis=d)
        else:
            pass
    return oarr

def polyfit2d(x, y, z, order=3):

    ncols = (order + 1) ** 2
    g = np.zeros((x.size, ncols))
    ij = itertools.product(range(order + 1), range(order + 1))
    for k, (i, j) in enumerate(ij):
        g[:, k] = x ** i * y ** j
    m, _, _, _ = np.linalg.lstsq(g, z)
    return m

def polyval2d(x, y, m):
    # x = x * 1.0
    #y = y * 1.0
    order = int(np.sqrt(len(m))) - 1
    ij = itertools.product(range(order + 1), range(order + 1))
    z = np.zeros_like(x, dtype='float64')
    for a, (i, j) in zip(m, ij):
        z += a * x ** i * y ** j
    return z

def coreg(arr1, arr2, sub=False):
    """
    Returns the coregistration offset between two complex arrays

    :author: Andreas Reigber
    :param arr1: The master image as (2D np.ndarray)
    :type arr1: complex
    :param arr2: The slave image as (2D np.ndarray)
    :type arr2: complex
    :param sub=True: Calculate subpixel offset.
    :type arr2: boolean
    :returns: tuple containing slave image offset in y and x
    """
    s = arr1.shape
    out1 = np.fft.rfft2(abs(arr1)).astype('complex64')
    out2 = np.fft.rfft2(abs(arr2)).astype('complex64')
    out1 *= np.conj(out2)
    out2 = np.fft.irfft2(out1).astype('float32')
    pos = out2.argmax()
    xoff = pos % s[1]
    yoff = pos / s[1]
    if xoff >= s[1] // 2:
        xoff = xoff - s[1]
    if yoff >= s[0] // 2:
        yoff = yoff - s[0]
    if sub is True:  # explicit polynominal expansion of the peak
        ysub = 0.5 * (out2[yoff - 1, xoff] - out2[yoff + 1, xoff]) / (out2[yoff - 1, xoff] +
                                                                      out2[yoff + 1, xoff] - 2 * out2[yoff, xoff])
        xsub = 0.5 * (out2[yoff, xoff - 1] - out2[yoff, xoff + 1]) / (out2[yoff, xoff - 1] +
                                                                      out2[yoff, xoff + 1] - 2 * out2[yoff, xoff])
        yoff += np.round(ysub, 2)
        xoff += np.round(xsub, 2)
    return yoff.astype('float32'), xoff.astype('float32')
<|endoftext|>


<commit_before>import ctypes
from .constants import rs_stream, rs_format

class Stream(object):
    """Stream object that stores all necessary information for interaction with librealsense.

    :param string name: name of the stream
    :param bool native: whether the stream is native or composite
    :param string stream: from the parsed rs_stream
    :param int width: name of the stream
    :param int height: name of the stream
    :param int format: from the parsed rs_format
    :param int fps: name of the stream
    """
    def __init__(self, name, native, stream, width, height, format, fps):
        super(Stream, self).__init__()
        self.name = name
        self.native = native
        self.stream = stream
        self.format = format
        self.width = width
        self.height = height
        self.fps = fps

class ColourStream(Stream):
    """Colour stream from device, with default parameters. See for possible combinations.
    """
    def __init__(self, width=640, height=480, fps=30):
        self.name = 'colour'
        self.native = True
        self.stream = rs_stream.RS_STREAM_COLOR
        self.format = rs_format.RS_FORMAT_RGB8,

        self.shape = (height, width, 3)
        self.dtype = ctypes.c_uint8

        super(ColourStream, self).__init__(name, native, stream, width, height, format, fps)

class DepthStream(Stream):
    def __init__(self, name='depth',
                       native=True,
                       stream=rs_stream.RS_STREAM_DEPTH,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_Z16,
                       fps=30):
        super(DepthStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width)
        self.dtype = ctypes.c_uint16

class PointStream(Stream):
    def __init__(self, name='points',
                       native=False,
                       stream=rs_stream.RS_STREAM_POINTS,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30):
        super(PointStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width, 3)
        self.dtype = ctypes.c_float

class CADStream(Stream):
    def __init__(self, name='cad',
                       native=False,
                       stream=rs_stream.RS_STREAM_COLOR_ALIGNED_TO_DEPTH,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30,
                       ):
        super(CADStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width, 3)
        self.dtype = ctypes.c_uint8

class DACStream(Stream):
    def __init__(self, name='dac',
                       native=False,
                       stream=rs_stream.RS_STREAM_DEPTH_ALIGNED_TO_COLOR,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30,
                       ):
        super(DACStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width)
        self.dtype = ctypes.c_uint16

<commit_msg>fix stream init<commit_after>import ctypes
from .constants import rs_stream, rs_format

class Stream(object):
    """Stream object that stores all necessary information for interaction with librealsense.

    :param string name: name of the stream
    :param bool native: whether the stream is native or composite
    :param string stream: from the parsed rs_stream
    :param int width: name of the stream
    :param int height: name of the stream
    :param int format: from the parsed rs_format
    :param int fps: name of the stream
    """
    def __init__(self, name, native, stream, width, height, format, fps):
        super(Stream, self).__init__()
        self.name = name
        self.native = native
        self.stream = stream
        self.format = format
        self.width = width
        self.height = height
        self.fps = fps

class ColourStream(Stream):
    """Colour stream from device, with default parameters. See for possible combinations.
    """
    def __init__(self, width=640, height=480, fps=30):
        self.name = 'colour'
        self.native = True
        self.stream = rs_stream.RS_STREAM_COLOR
        self.format = rs_format.RS_FORMAT_RGB8,
        self.shape = (height, width, 3)
        self.dtype = ctypes.c_uint8

        super(ColourStream, self).__init__(self.name, self.native, self.stream, width, height, self.format, fps)

class DepthStream(Stream):
    def __init__(self, name='depth',
                       native=True,
                       stream=rs_stream.RS_STREAM_DEPTH,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_Z16,
                       fps=30):
        super(DepthStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width)
        self.dtype = ctypes.c_uint16

class PointStream(Stream):
    def __init__(self, name='points',
                       native=False,
                       stream=rs_stream.RS_STREAM_POINTS,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30):
        super(PointStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width, 3)
        self.dtype = ctypes.c_float

class CADStream(Stream):
    def __init__(self, name='cad',
                       native=False,
                       stream=rs_stream.RS_STREAM_COLOR_ALIGNED_TO_DEPTH,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30,
                       ):
        super(CADStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width, 3)
        self.dtype = ctypes.c_uint8

class DACStream(Stream):
    def __init__(self, name='dac',
                       native=False,
                       stream=rs_stream.RS_STREAM_DEPTH_ALIGNED_TO_COLOR,
                       width=640,
                       height=480,
                       format=rs_format.RS_FORMAT_XYZ32F,
                       fps=30,
                       ):
        super(DACStream, self).__init__(name, native, stream, width, height, format, fps)
        self.shape = (height, width)
        self.dtype = ctypes.c_uint16

<|endoftext|>


<commit_before>import sys
import warnings

if sys.version_info >= (3, 0):
    from io import StringIO
    from urllib.request import urlopen
    from urllib.error import HTTPError
else:
    from cStringIO import StringIO
    from urllib2 import urlopen, HTTPError

import os
import re
import shutil
import subprocess as sp
import xml.etree.ElementTree as ET
import zipfile as zf
from ftplib import FTP
from time import strftime, gmtime
from xml.dom import minidom
from os.path import expanduser

import pyroSAR
from pyroSAR import identify
from pyroSAR.ancillary import dissolve, finder, which
from pyroSAR.spatial import gdal_translate
from .._dev_config import LOOKUP

def parse_recipe(name):
    name = name if name.endswith('.xml') else name + '.xml'
    absname = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'recipes', name)
    with open(absname, 'r') as workflow:
        tree = ET.fromstring(workflow.read())
    return tree

def parse_node(name):
    name = name if name.endswith('.xml') else name + '.xml'
    absname = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'recipes', 'nodes', name)
    with open(absname, 'r') as workflow:
        tree = ET.fromstring(workflow.read())
    return tree

def parse_suffix(workflow):
    nodes = workflow.findall('node')
    suffix = '_'.join(filter(None, [LOOKUP.snap.suffix[x] for x in [y.attrib['id'] for y in nodes]]))
    return suffix

def insert_node(workflow, predecessor_id, node):
    predecessor = workflow.find('.//node[@id="{}"]'.format(predecessor_id))
    position = list(workflow).index(predecessor) + 1
    workflow.insert(position, node)
    newnode = workflow[position]
    newnode.find('.//sources/sourceProduct').attrib['refid'] = predecessor.attrib['id']
    workflow[position + 1].find('.//sources/sourceProduct').attrib['refid'] = newnode.attrib['id']

def write_recipe(recipe, outfile):
    outfile = outfile if outfile.endswith('.xml') else outfile + '.xml'
    rough_string = ET.tostring(recipe, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    with open(outfile, 'w') as out:
        out.write(reparsed.toprettyxml(indent='\t', newl=''))

def getOrbitContentVersions(contentVersion):
    return dict(
        [re.split('\s*=\s*', x.strip('\r')) for x in contentVersion.read().split('\n') if re.search('^[0-9]{4}', x)])

class GetAuxdata:
    def __init__(self, datasets, scenes):
        self.datasets = datasets
        self.scenes = [identify(scene) if isinstance(scene, str) else scene for scene in scenes]
        self.sensors = list(set([scene.sensor for scene in scenes]))

        try:
            self.auxDataPath = os.path.join(os.environ['HOME'], '.snap/auxdata')
        except KeyError:
            self.auxDataPath = os.path.join(os.environ['USERPROFILE'], '.snap/auxdata')

    def srtm_1sec_hgt(self):
        pass
        # Wird nicht benutzt?

#        for dataset in self.datasets:
#            files = [x.replace('hgt', 'SRTMGL1.hgt.zip') for x in
#                     list(set(dissolve([scene.getHGT() for scene in self.scenes])))]

def getAuxdata(datasets, scenes):
    auxDataPath = os.path.join(expanduser("~"), '.snap/auxdata')

    scenes = [identify(scene) if isinstance(scene, str) else scene for scene in scenes]
    sensors = list(set([scene.sensor for scene in scenes]))
    for dataset in datasets:
        if dataset == 'SRTM 1Sec HGT':
            files = [x.replace('hgt', 'SRTMGL1.hgt.zip') for x in
                     list(set(dissolve([scene.getHGT() for scene in scenes])))]
            for file in files:
                infile = os.path.join('http://step.esa.int/auxdata/dem/SRTMGL1', file)
                outfile = os.path.join(auxDataPath, 'dem/SRTM 1Sec HGT', file)
                if not os.path.isfile(outfile):
                    print(infile)
                    try:
                        input = urlopen(infile)
                    except HTTPError:
                        print('-> not available')
                        continue
                    with open(outfile, 'wb') as output:
                        output.write(input.read())
                    input.close()
        elif dataset == 'POEORB':
            for sensor in sensors:
                if re.search('S1[AB]', sensor):

                    dates = [(scene.start[:4], scene.start[4:6]) for scene in scenes]
                    years = list(set([x[0] for x in dates]))

                    remote_contentVersion = urlopen(
                        'http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/remote_contentVersion.txt')
                    versions_remote = getOrbitContentVersions(remote_contentVersion)

                    for year in years:
                        dir_orb = os.path.join(auxDataPath, 'Orbits/Sentinel-1/POEORB', year)

                        if not os.path.isdir(dir_orb):
                            os.makedirs(dir_orb)
                        contentVersionFile = os.path.join(dir_orb, 'contentVersion.txt')

                        if os.path.isfile(contentVersionFile):
                            contentVersion = open(contentVersionFile, 'r+')
                            versions_local = getOrbitContentVersions(contentVersion)
                        else:
                            contentVersion = open(contentVersionFile, 'w')
                            versions_local = {}

                        combine = dict(set(versions_local.items()) & set(versions_remote.items()))

                        dates_select = [x for x in dates if x[0] == year]
                        months = list(set([x[1] for x in dates_select]))

                        orb_ids = sorted(
                            [x for x in ['{}-{}.zip'.format(year, month) for month in months] if not x in combine])

                        if len(orb_ids) > 0:
                            contentVersion.write('#\n#{}\n'.format(strftime('%a %b %d %H:%M:%S %Z %Y', gmtime())))

                            for orb_id in orb_ids:
                                orb_remote = urlopen(
                                    'http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/{}'.format(orb_id))
                                orb_remote_stream = zf.ZipFile(StringIO(orb_remote.read()), 'r')
                                orb_remote.close()

                                targets = [x for x in orb_remote_stream.namelist() if
                                           not os.path.isfile(os.path.join(dir_orb, x))]
                                orb_remote_stream.extractall(dir_orb, targets)
                                orb_remote_stream.close()

                                versions_local[orb_id] = versions_remote[orb_id]

                                for key, val in versions_local.iteritems():
                                    contentVersion.write('{}={}\n'.format(key, val))

                        contentVersion.close()
                    remote_contentVersion.close()
                else:
                    print('not implemented yet')
        elif dataset == 'Delft Precise Orbits':
            path_server = 'dutlru2.lr.tudelft.nl'
            subdirs = {'ASAR:': 'ODR.ENVISAT1/eigen-cg03c', 'ERS1': 'ODR.ERS-1/dgm-e04', 'ERS2': 'ODR.ERS-2/dgm-e04'}
            ftp = FTP(path_server)
            ftp.login()
            for sensor in sensors:
                if sensor in subdirs.keys():
                    path_target = os.path.join('pub/orbits', subdirs[sensor])
                    path_local = os.path.join(auxDataPath, 'Orbits/Delft Precise Orbits', subdirs[sensor])
                    ftp.cwd(path_target)
                    for item in ftp.nlst():
                        ftp.retrbinary('RETR ' + item, open(os.path.join(path_local, item), 'wb').write)
            ftp.quit()
        else:
            print('not implemented yet')

def gpt(xmlfile):
    """
    wrapper for ESA SNAP Graph Processing Tool GPT
    input is a readily formatted workflow xml file as created by function geocode in module snap.util
    """
    try:
        snap_exec = ExamineSnap().path
    except AttributeError:
        raise RuntimeError('could not find SNAP executable')
    gpt_exec = os.path.join(os.path.dirname(snap_exec), 'gpt')

    with open(xmlfile, 'r') as infile:
        workflow = ET.fromstring(infile.read())
    write = workflow.find('.//node[@id="Write"]')
    outname = write.find('.//parameters/file').text
    outdir = os.path.dirname(outname)
    format = write.find('.//parameters/formatName').text
    infile = workflow.find('.//node[@id="Read"]/parameters/file').text

    if format == 'GeoTiff-BigTIFF':
        cmd = [gpt_exec,
               # '-Dsnap.dataio.reader.tileWidth=*',
               # '-Dsnap.dataio.reader.tileHeight=1',
               '-Dsnap.dataio.bigtiff.tiling.width=256',
               '-Dsnap.dataio.bigtiff.tiling.height=256',
               # '-Dsnap.dataio.bigtiff.compression.type=LZW',
               # '-Dsnap.dataio.bigtiff.compression.quality=0.75',
               xmlfile]
    else:
        cmd = [gpt_exec, xmlfile]

    proc = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
    out, err = proc.communicate()
    if proc.returncode != 0:
        if os.path.isfile(outname + '.tif'):
            os.remove(outname + '.tif')
        elif os.path.isdir(outname):
            shutil.rmtree(outname)
        print(out + err)
        print('failed: {}'.format(os.path.basename(infile)))
        err_match = re.search('Error: (.*)\n', out + err)
        errmessage = err_match.group(1) if err_match else err
        raise RuntimeError(errmessage)

    if format == 'ENVI':
        id = pyroSAR.identify(infile)
        suffix = parse_suffix(workflow)
        for item in finder(outname, ['*.img']):
            pol = re.search('[HV]{2}', item).group()
            name_new = os.path.join(outdir, '{}_{}_{}.tif'.format(id.outname_base(), pol, suffix))
            translateoptions = {'options': ['-q', '-co', 'INTERLEAVE=BAND', '-co', 'TILED=YES'], 'format': 'GTiff'}
            gdal_translate(item, name_new, translateoptions)
        shutil.rmtree(outname)

class ExamineSnap(object):
    """
    Class to check if snap is installed. This will be called with snap.__init__
    as snap_config. If you are running multiple snap versions or the package can
    not find the snap executable, you can set an path via: snap_config.set_path("path")
    """

    def __init__(self):

        candidates = ['snap64.exe', 'snap32.exe', 'snap.exe', 'snap']
        executables = list(filter(None, [which(x) for x in candidates]))

        if len(executables) == 0:
            warnings.warn(
                'Cannot find SNAP installation. You can download it from '
                'http://step.esa.int/main/download/ or you can specify a path with '
                'snap_config.set_path(path_to_snap)', UserWarning)

        elif len(executables) > 1:
            raise warnings.warn(
                'There are more than one instances of SNAP installed. '
                'Define which one you would like to use with ExamineSnap.set_path(...)', UserWarning)

        self.path = executables[0]

        if os.path.islink(self.path):
            self.path = os.path.realpath(self.path)

        self.auxdatapath = os.path.join(expanduser('~'), '.snap/auxdata')

        self.__get_etc()
        self.__read_config()

    def __get_etc(self):
        try:
            self.etc = os.path.join(os.path.dirname(os.path.dirname(self.path)), 'etc')
            self.auxdata = os.listdir(self.etc)
            self.config_path = os.path.join(self.etc, [s for s in self.auxdata if 'snap.auxdata.properties' in s][0])

        except OSError:
            raise AssertionError('ETC directory is not existent.')

    def set_path(self, path):
        self.path = os.path.abspath(path)
        self.__get_etc()

    def __read_config(self):
        with open(self.config_path) as config:
            self.config = []
            for line in config:
                self.config.append(line)
<commit_msg>[snap.auxil.ExamineSnap] simplified code for clarity<commit_after>import sys
import warnings

if sys.version_info >= (3, 0):
    from io import StringIO
    from urllib.request import urlopen
    from urllib.error import HTTPError
else:
    from cStringIO import StringIO
    from urllib2 import urlopen, HTTPError

import os
import re
import shutil
import subprocess as sp
import xml.etree.ElementTree as ET
import zipfile as zf
from ftplib import FTP
from time import strftime, gmtime
from xml.dom import minidom
from os.path import expanduser

import pyroSAR
from pyroSAR import identify
from pyroSAR.ancillary import dissolve, finder, which
from pyroSAR.spatial import gdal_translate
from .._dev_config import LOOKUP

def parse_recipe(name):
    name = name if name.endswith('.xml') else name + '.xml'
    absname = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'recipes', name)
    with open(absname, 'r') as workflow:
        tree = ET.fromstring(workflow.read())
    return tree

def parse_node(name):
    name = name if name.endswith('.xml') else name + '.xml'
    absname = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'recipes', 'nodes', name)
    with open(absname, 'r') as workflow:
        tree = ET.fromstring(workflow.read())
    return tree

def parse_suffix(workflow):
    nodes = workflow.findall('node')
    suffix = '_'.join(filter(None, [LOOKUP.snap.suffix[x] for x in [y.attrib['id'] for y in nodes]]))
    return suffix

def insert_node(workflow, predecessor_id, node):
    predecessor = workflow.find('.//node[@id="{}"]'.format(predecessor_id))
    position = list(workflow).index(predecessor) + 1
    workflow.insert(position, node)
    newnode = workflow[position]
    newnode.find('.//sources/sourceProduct').attrib['refid'] = predecessor.attrib['id']
    workflow[position + 1].find('.//sources/sourceProduct').attrib['refid'] = newnode.attrib['id']

def write_recipe(recipe, outfile):
    outfile = outfile if outfile.endswith('.xml') else outfile + '.xml'
    rough_string = ET.tostring(recipe, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    with open(outfile, 'w') as out:
        out.write(reparsed.toprettyxml(indent='\t', newl=''))

def getOrbitContentVersions(contentVersion):
    return dict(
        [re.split('\s*=\s*', x.strip('\r')) for x in contentVersion.read().split('\n') if re.search('^[0-9]{4}', x)])

class GetAuxdata:
    def __init__(self, datasets, scenes):
        self.datasets = datasets
        self.scenes = [identify(scene) if isinstance(scene, str) else scene for scene in scenes]
        self.sensors = list(set([scene.sensor for scene in scenes]))

        try:
            self.auxDataPath = os.path.join(os.environ['HOME'], '.snap/auxdata')
        except KeyError:
            self.auxDataPath = os.path.join(os.environ['USERPROFILE'], '.snap/auxdata')

    def srtm_1sec_hgt(self):
        pass
        # Wird nicht benutzt?

#        for dataset in self.datasets:
#            files = [x.replace('hgt', 'SRTMGL1.hgt.zip') for x in
#                     list(set(dissolve([scene.getHGT() for scene in self.scenes])))]

def getAuxdata(datasets, scenes):
    auxDataPath = os.path.join(expanduser("~"), '.snap/auxdata')

    scenes = [identify(scene) if isinstance(scene, str) else scene for scene in scenes]
    sensors = list(set([scene.sensor for scene in scenes]))
    for dataset in datasets:
        if dataset == 'SRTM 1Sec HGT':
            files = [x.replace('hgt', 'SRTMGL1.hgt.zip') for x in
                     list(set(dissolve([scene.getHGT() for scene in scenes])))]
            for file in files:
                infile = os.path.join('http://step.esa.int/auxdata/dem/SRTMGL1', file)
                outfile = os.path.join(auxDataPath, 'dem/SRTM 1Sec HGT', file)
                if not os.path.isfile(outfile):
                    print(infile)
                    try:
                        input = urlopen(infile)
                    except HTTPError:
                        print('-> not available')
                        continue
                    with open(outfile, 'wb') as output:
                        output.write(input.read())
                    input.close()
        elif dataset == 'POEORB':
            for sensor in sensors:
                if re.search('S1[AB]', sensor):

                    dates = [(scene.start[:4], scene.start[4:6]) for scene in scenes]
                    years = list(set([x[0] for x in dates]))

                    remote_contentVersion = urlopen(
                        'http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/remote_contentVersion.txt')
                    versions_remote = getOrbitContentVersions(remote_contentVersion)

                    for year in years:
                        dir_orb = os.path.join(auxDataPath, 'Orbits/Sentinel-1/POEORB', year)

                        if not os.path.isdir(dir_orb):
                            os.makedirs(dir_orb)
                        contentVersionFile = os.path.join(dir_orb, 'contentVersion.txt')

                        if os.path.isfile(contentVersionFile):
                            contentVersion = open(contentVersionFile, 'r+')
                            versions_local = getOrbitContentVersions(contentVersion)
                        else:
                            contentVersion = open(contentVersionFile, 'w')
                            versions_local = {}

                        combine = dict(set(versions_local.items()) & set(versions_remote.items()))

                        dates_select = [x for x in dates if x[0] == year]
                        months = list(set([x[1] for x in dates_select]))

                        orb_ids = sorted(
                            [x for x in ['{}-{}.zip'.format(year, month) for month in months] if not x in combine])

                        if len(orb_ids) > 0:
                            contentVersion.write('#\n#{}\n'.format(strftime('%a %b %d %H:%M:%S %Z %Y', gmtime())))

                            for orb_id in orb_ids:
                                orb_remote = urlopen(
                                    'http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/{}'.format(orb_id))
                                orb_remote_stream = zf.ZipFile(StringIO(orb_remote.read()), 'r')
                                orb_remote.close()

                                targets = [x for x in orb_remote_stream.namelist() if
                                           not os.path.isfile(os.path.join(dir_orb, x))]
                                orb_remote_stream.extractall(dir_orb, targets)
                                orb_remote_stream.close()

                                versions_local[orb_id] = versions_remote[orb_id]

                                for key, val in versions_local.iteritems():
                                    contentVersion.write('{}={}\n'.format(key, val))

                        contentVersion.close()
                    remote_contentVersion.close()
                else:
                    print('not implemented yet')
        elif dataset == 'Delft Precise Orbits':
            path_server = 'dutlru2.lr.tudelft.nl'
            subdirs = {'ASAR:': 'ODR.ENVISAT1/eigen-cg03c', 'ERS1': 'ODR.ERS-1/dgm-e04', 'ERS2': 'ODR.ERS-2/dgm-e04'}
            ftp = FTP(path_server)
            ftp.login()
            for sensor in sensors:
                if sensor in subdirs.keys():
                    path_target = os.path.join('pub/orbits', subdirs[sensor])
                    path_local = os.path.join(auxDataPath, 'Orbits/Delft Precise Orbits', subdirs[sensor])
                    ftp.cwd(path_target)
                    for item in ftp.nlst():
                        ftp.retrbinary('RETR ' + item, open(os.path.join(path_local, item), 'wb').write)
            ftp.quit()
        else:
            print('not implemented yet')

def gpt(xmlfile):
    """
    wrapper for ESA SNAP Graph Processing Tool GPT
    input is a readily formatted workflow xml file as created by function geocode in module snap.util
    """
    try:
        snap_exec = ExamineSnap().path
    except AttributeError:
        raise RuntimeError('could not find SNAP executable')
    gpt_exec = os.path.join(os.path.dirname(snap_exec), 'gpt')

    with open(xmlfile, 'r') as infile:
        workflow = ET.fromstring(infile.read())
    write = workflow.find('.//node[@id="Write"]')
    outname = write.find('.//parameters/file').text
    outdir = os.path.dirname(outname)
    format = write.find('.//parameters/formatName').text
    infile = workflow.find('.//node[@id="Read"]/parameters/file').text

    if format == 'GeoTiff-BigTIFF':
        cmd = [gpt_exec,
               # '-Dsnap.dataio.reader.tileWidth=*',
               # '-Dsnap.dataio.reader.tileHeight=1',
               '-Dsnap.dataio.bigtiff.tiling.width=256',
               '-Dsnap.dataio.bigtiff.tiling.height=256',
               # '-Dsnap.dataio.bigtiff.compression.type=LZW',
               # '-Dsnap.dataio.bigtiff.compression.quality=0.75',
               xmlfile]
    else:
        cmd = [gpt_exec, xmlfile]

    proc = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
    out, err = proc.communicate()
    if proc.returncode != 0:
        if os.path.isfile(outname + '.tif'):
            os.remove(outname + '.tif')
        elif os.path.isdir(outname):
            shutil.rmtree(outname)
        print(out + err)
        print('failed: {}'.format(os.path.basename(infile)))
        err_match = re.search('Error: (.*)\n', out + err)
        errmessage = err_match.group(1) if err_match else err
        raise RuntimeError(errmessage)

    if format == 'ENVI':
        id = pyroSAR.identify(infile)
        suffix = parse_suffix(workflow)
        for item in finder(outname, ['*.img']):
            pol = re.search('[HV]{2}', item).group()
            name_new = os.path.join(outdir, '{}_{}_{}.tif'.format(id.outname_base(), pol, suffix))
            translateoptions = {'options': ['-q', '-co', 'INTERLEAVE=BAND', '-co', 'TILED=YES'], 'format': 'GTiff'}
            gdal_translate(item, name_new, translateoptions)
        shutil.rmtree(outname)

class ExamineSnap(object):
    """
    Class to check if snap is installed. This will be called with snap.__init__
    as snap_config. If you are running multiple snap versions or the package can
    not find the snap executable, you can set an path via: snap_config.set_path("path")
    """

    def __init__(self):

        candidates = ['snap64.exe', 'snap32.exe', 'snap.exe', 'snap']
        executables = list(filter(None, [which(x) for x in candidates]))

        if len(executables) == 0:

            self.path = None

            warnings.warn(
                'Cannot find SNAP installation. You can download it from '
                'http://step.esa.int/main/download/ or you can specify a path with '
                'snap_config.set_path(path_to_snap)', UserWarning)
        else:
            self.path = executables[0]

            if os.path.islink(self.path):
                self.path = os.path.realpath(self.path)

            if len(executables) > 1:
                raise warnings.warn(
                    'There are more than one instances of SNAP installed:\n{options}\n'
                    'Using the first option:\n{select}\n'
                    'You can define a different one with ExamineSnap.set_path()'
                    ''.format(options='\n'.join(executables), select=self.path), UserWarning)

            self.__get_etc()
            self.__read_config()

        self.auxdatapath = os.path.join(expanduser('~'), '.snap/auxdata')

    def __get_etc(self):
        try:
            self.etc = os.path.join(os.path.dirname(os.path.dirname(self.path)), 'etc')
            self.auxdata = os.listdir(self.etc)
            self.config_path = os.path.join(self.etc, [s for s in self.auxdata if 'snap.auxdata.properties' in s][0])

        except OSError:
            raise AssertionError('ETC directory is not existent.')

    def __read_config(self):
        with open(self.config_path) as config:
            self.config = []
            for line in config:
                self.config.append(line)
<|endoftext|>


<commit_before>import inspect
import warnings
import pysb
import sympy

class BngGenerator(object):

    def __init__(self, model):
        self.model = model
        if self.model.has_synth_deg():
            self.model.enable_synth_deg()
        self.__content = None

    def get_content(self):
        if self.__content == None:
            self.generate_content()
        return self.__content

    def generate_content(self):
        self.__content = ''
        self.generate_parameters()
        self.generate_compartments()
        self.generate_molecule_types()
        self.generate_reaction_rules()
        self.generate_observables()
        self.generate_species()

    def generate_parameters(self):
        if not self.model.parameters:
            return
        self.__content += "begin parameters\n"
        max_length = max(len(p.name) for p in
                         self.model.parameters | self.model.expressions)
        for p in self.model.parameters:
            self.__content += (("  %-" + str(max_length) + "s   %e\n") %
                               (p.name, p.value))
        for e in self.model.expressions:
            self.__content += (("  %-" + str(max_length) + "s   %s\n") %
                               (e.name, sympy.ccode(e.expr)))
        self.__content += "end parameters\n\n"

    def generate_compartments(self):
        if not self.model.compartments:
            return
        self.__content += "begin compartments\n"
        for c in self.model.compartments:
            if c.parent is None:
                parent_name = ''
            else:
                parent_name = c.parent.name
            if c.size is None:
                size = "1.0"
            else:
                size = c.size.name
            self.__content += ("  %s  %d  %s  %s\n") % (c.name, c.dimension, size, parent_name)
        self.__content += "end compartments\n\n"

    def generate_molecule_types(self):
        if not self.model.monomers:
            return
        self.__content += "begin molecule types\n"
        for m in self.model.monomers:
            site_code = ','.join([format_monomer_site(m, s) for s in m.sites])
            self.__content += "  %s(%s)\n" % (m.name, site_code)
        self.__content += "end molecule types\n\n"

    def generate_reaction_rules(self):
        if not self.model.rules:
            warn_caller("Model does not contain any rules")
            return
        self.__content += "begin reaction rules\n"
        max_length = max(len(r.name) for r in self.model.rules) + 1  # +1 for the colon
        for r in self.model.rules:
            label = r.name + ':'
            react_p = r.reactant_pattern
            prod_p = r.product_pattern
            if r.is_synth():
                source_mp = self.model.monomers['__source']()
                react_p = pysb.core.as_reaction_pattern(source_mp)
                prod_p += source_mp
            if r.is_deg():
                sink_mp = self.model.monomers['__sink']()
                prod_p = pysb.core.as_reaction_pattern(sink_mp)
            reactants_code = format_reactionpattern(react_p)
            products_code = format_reactionpattern(prod_p)
            arrow = '->'
            if r.is_reversible:
                arrow = '<->'
            self.__content += ("  %-" + str(max_length) + "s  %s %s %s    %s") % \
                (label, reactants_code, arrow, products_code, r.rate_forward.name)
            if r.is_reversible:
                self.__content += ', %s' % r.rate_reverse.name
            if r.delete_molecules:
                self.__content += ' DeleteMolecules'
            if r.move_connected:
                self.__content += ' MoveConnected'
            self.__content += "\n"
        self.__content += "end reaction rules\n\n"

    def generate_observables(self):
        if not self.model.observables:
            return
        max_length = max(len(name) for name in self.model.observables.keys())
        self.__content += "begin observables\n"
        for obs in self.model.observables:
            observable_code = format_reactionpattern(obs.reaction_pattern,
                                                     for_observable=True)
            self.__content += ("  %s %-" + str(max_length) + "s   %s\n") % \
                              (obs.match.title(), obs.name, observable_code)
        self.__content += "end observables\n\n"

    def generate_species(self):
        if not self.model.initial_conditions:
            warn_caller("Model does not contain any initial conditions")
            return
        species_codes = [format_complexpattern(cp) for cp, param in self.model.initial_conditions]
        max_length = max(len(code) for code in species_codes)
        self.__content += "begin species\n"
        for i, code in enumerate(species_codes):
            param = self.model.initial_conditions[i][1]
            self.__content += ("  %-" + str(max_length) + "s   %s\n") % (code, param.name)
        self.__content += "end species\n"

def format_monomer_site(monomer, site):
    ret = site
    if monomer.site_states.has_key(site):
        for state in monomer.site_states[site]:
            ret += '~' + state
    return ret

def format_reactionpattern(rp, for_observable=False):
    if for_observable is False:
        delimiter = ' + '
    else:
        delimiter = ' '
    return delimiter.join([format_complexpattern(cp) for cp in rp.complex_patterns])

def format_complexpattern(cp):
    ret = '.'.join([format_monomerpattern(mp) for mp in cp.monomer_patterns])
    if cp.compartment is not None:
        ret = '@%s:%s' % (cp.compartment.name, ret)
    if cp.match_once:
        ret = '{MatchOnce}' + ret
    return ret

def format_monomerpattern(mp):
    # sort sites in the same order given in the original Monomer
    site_conditions = sorted(mp.site_conditions.items(),
                             key=lambda x: mp.monomer.sites.index(x[0]))
    site_pattern_code = ','.join([format_site_condition(site, state) for (site, state) in site_conditions])
    ret = '%s(%s)' % (mp.monomer.name, site_pattern_code)
    if mp.compartment is not None:
        ret = '%s@%s' % (ret, mp.compartment.name)
    return ret

def format_site_condition(site, state):
    # empty
    if state == None:
        state_code = ''
    # single bond
    elif type(state) == int:
        state_code = '!' + str(state)
    # multiple bonds
    elif type(state) == list and all(isinstance(s, int) for s in state):
        state_code = ''.join('!%d' % s for s in state)
    # state
    elif type(state) == str:
        state_code = '~' + state
    # state AND single bond
    elif type(state) == tuple:
        # bond is wildcard (zero or more unspecified bonds)
        if state[1] == pysb.WILD:
            state = (state[0], '?')
        state_code = '~%s!%s' % state
    # one or more unspecified bonds
    elif state is pysb.ANY:
        state_code = '!+'
    # anything at all (usually you can leverage don't-care-don't-write, but in
    # some cases such as when a rule explicitly sets the state of site A but
    # conditions on site B, site A on the reactant side must be set to WILD)
    elif state is pysb.WILD:
        state_code = '!?'
    else:
        raise Exception("BNG generator has encountered an unknown element in a rule pattern site condition.")
    return '%s%s' % (site, state_code)

def warn_caller(message):
    caller_frame = inspect.currentframe()
    # walk up through the stack until we are outside of pysb
    stacklevel = 1
    module = inspect.getmodule(caller_frame)
    while module and module.__name__.startswith('pysb.'):
        stacklevel += 1
        caller_frame = caller_frame.f_back
        module = inspect.getmodule(caller_frame)
    warnings.warn(message, stacklevel=stacklevel)
<commit_msg>make bng generator handle models with expressions but no parameters<commit_after>import inspect
import warnings
import pysb
import sympy

class BngGenerator(object):

    def __init__(self, model):
        self.model = model
        if self.model.has_synth_deg():
            self.model.enable_synth_deg()
        self.__content = None

    def get_content(self):
        if self.__content == None:
            self.generate_content()
        return self.__content

    def generate_content(self):
        self.__content = ''
        self.generate_parameters()
        self.generate_compartments()
        self.generate_molecule_types()
        self.generate_reaction_rules()
        self.generate_observables()
        self.generate_species()

    def generate_parameters(self):
        if not self.model.parameters and not self.model.expressions:
            return
        self.__content += "begin parameters\n"
        max_length = max(len(p.name) for p in
                         self.model.parameters | self.model.expressions)
        for p in self.model.parameters:
            self.__content += (("  %-" + str(max_length) + "s   %e\n") %
                               (p.name, p.value))
        for e in self.model.expressions:
            self.__content += (("  %-" + str(max_length) + "s   %s\n") %
                               (e.name, sympy.ccode(e.expr)))
        self.__content += "end parameters\n\n"

    def generate_compartments(self):
        if not self.model.compartments:
            return
        self.__content += "begin compartments\n"
        for c in self.model.compartments:
            if c.parent is None:
                parent_name = ''
            else:
                parent_name = c.parent.name
            if c.size is None:
                size = "1.0"
            else:
                size = c.size.name
            self.__content += ("  %s  %d  %s  %s\n") % (c.name, c.dimension, size, parent_name)
        self.__content += "end compartments\n\n"

    def generate_molecule_types(self):
        if not self.model.monomers:
            return
        self.__content += "begin molecule types\n"
        for m in self.model.monomers:
            site_code = ','.join([format_monomer_site(m, s) for s in m.sites])
            self.__content += "  %s(%s)\n" % (m.name, site_code)
        self.__content += "end molecule types\n\n"

    def generate_reaction_rules(self):
        if not self.model.rules:
            warn_caller("Model does not contain any rules")
            return
        self.__content += "begin reaction rules\n"
        max_length = max(len(r.name) for r in self.model.rules) + 1  # +1 for the colon
        for r in self.model.rules:
            label = r.name + ':'
            react_p = r.reactant_pattern
            prod_p = r.product_pattern
            if r.is_synth():
                source_mp = self.model.monomers['__source']()
                react_p = pysb.core.as_reaction_pattern(source_mp)
                prod_p += source_mp
            if r.is_deg():
                sink_mp = self.model.monomers['__sink']()
                prod_p = pysb.core.as_reaction_pattern(sink_mp)
            reactants_code = format_reactionpattern(react_p)
            products_code = format_reactionpattern(prod_p)
            arrow = '->'
            if r.is_reversible:
                arrow = '<->'
            self.__content += ("  %-" + str(max_length) + "s  %s %s %s    %s") % \
                (label, reactants_code, arrow, products_code, r.rate_forward.name)
            if r.is_reversible:
                self.__content += ', %s' % r.rate_reverse.name
            if r.delete_molecules:
                self.__content += ' DeleteMolecules'
            if r.move_connected:
                self.__content += ' MoveConnected'
            self.__content += "\n"
        self.__content += "end reaction rules\n\n"

    def generate_observables(self):
        if not self.model.observables:
            return
        max_length = max(len(name) for name in self.model.observables.keys())
        self.__content += "begin observables\n"
        for obs in self.model.observables:
            observable_code = format_reactionpattern(obs.reaction_pattern,
                                                     for_observable=True)
            self.__content += ("  %s %-" + str(max_length) + "s   %s\n") % \
                              (obs.match.title(), obs.name, observable_code)
        self.__content += "end observables\n\n"

    def generate_species(self):
        if not self.model.initial_conditions:
            warn_caller("Model does not contain any initial conditions")
            return
        species_codes = [format_complexpattern(cp) for cp, param in self.model.initial_conditions]
        max_length = max(len(code) for code in species_codes)
        self.__content += "begin species\n"
        for i, code in enumerate(species_codes):
            param = self.model.initial_conditions[i][1]
            self.__content += ("  %-" + str(max_length) + "s   %s\n") % (code, param.name)
        self.__content += "end species\n"

def format_monomer_site(monomer, site):
    ret = site
    if monomer.site_states.has_key(site):
        for state in monomer.site_states[site]:
            ret += '~' + state
    return ret

def format_reactionpattern(rp, for_observable=False):
    if for_observable is False:
        delimiter = ' + '
    else:
        delimiter = ' '
    return delimiter.join([format_complexpattern(cp) for cp in rp.complex_patterns])

def format_complexpattern(cp):
    ret = '.'.join([format_monomerpattern(mp) for mp in cp.monomer_patterns])
    if cp.compartment is not None:
        ret = '@%s:%s' % (cp.compartment.name, ret)
    if cp.match_once:
        ret = '{MatchOnce}' + ret
    return ret

def format_monomerpattern(mp):
    # sort sites in the same order given in the original Monomer
    site_conditions = sorted(mp.site_conditions.items(),
                             key=lambda x: mp.monomer.sites.index(x[0]))
    site_pattern_code = ','.join([format_site_condition(site, state) for (site, state) in site_conditions])
    ret = '%s(%s)' % (mp.monomer.name, site_pattern_code)
    if mp.compartment is not None:
        ret = '%s@%s' % (ret, mp.compartment.name)
    return ret

def format_site_condition(site, state):
    # empty
    if state == None:
        state_code = ''
    # single bond
    elif type(state) == int:
        state_code = '!' + str(state)
    # multiple bonds
    elif type(state) == list and all(isinstance(s, int) for s in state):
        state_code = ''.join('!%d' % s for s in state)
    # state
    elif type(state) == str:
        state_code = '~' + state
    # state AND single bond
    elif type(state) == tuple:
        # bond is wildcard (zero or more unspecified bonds)
        if state[1] == pysb.WILD:
            state = (state[0], '?')
        state_code = '~%s!%s' % state
    # one or more unspecified bonds
    elif state is pysb.ANY:
        state_code = '!+'
    # anything at all (usually you can leverage don't-care-don't-write, but in
    # some cases such as when a rule explicitly sets the state of site A but
    # conditions on site B, site A on the reactant side must be set to WILD)
    elif state is pysb.WILD:
        state_code = '!?'
    else:
        raise Exception("BNG generator has encountered an unknown element in a rule pattern site condition.")
    return '%s%s' % (site, state_code)

def warn_caller(message):
    caller_frame = inspect.currentframe()
    # walk up through the stack until we are outside of pysb
    stacklevel = 1
    module = inspect.getmodule(caller_frame)
    while module and module.__name__.startswith('pysb.'):
        stacklevel += 1
        caller_frame = caller_frame.f_back
        module = inspect.getmodule(caller_frame)
    warnings.warn(message, stacklevel=stacklevel)
<|endoftext|>