<commit_before>#!/usr/bin/env python
# -- Content-Encoding: UTF-8 --
"""
COHORTE Remote Services: JABSORB-RPC, i.e. JSON-RPC in Jabsorb format

:author: Thomas Calmant
:copyright: Copyright 2013, isandlaTech
:license: Apache License 2.0
:version: 1.1
:status: Beta

..

    Copyright 2013 isandlaTech

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

# Documentation strings format
__docformat__ = "restructuredtext en"

# Module version
__version_info__ = (1, 1, 0)
__version__ = ".".join(str(x) for x in __version_info__)

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

# JSON-RPC
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCDispatcher, \
    NoMulticallResult
import jsonrpclib.jsonrpc as jsonrpclib

# Cohorte
import cohorte.java.jabsorb as jabsorb

# iPOPO Decorators
from pelix.ipopo.decorators import ComponentFactory, Provides, Validate, \
    Invalidate, Requires, Property
import pelix.framework
import pelix.http
import pelix.remote.beans
from pelix.remote import RemoteServiceError
from pelix.utilities import to_str

# Standard library
import logging
import socket
import threading
import uuid

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

JABSORB_CONFIG = 'ecf.jabsorb'
""" Remote Service configuration constant """

PROP_ENDPOINT_NAME = '{0}.name'.format(JABSORB_CONFIG)
""" Name of the endpoint """

PROP_HTTP_ACCESSES = '{0}.accesses'.format(JABSORB_CONFIG)
""" HTTP accesses (comma-separated String) """

HOST_SERVLET_PATH = "/JABSORB-RPC"
""" Default servlet path """

_logger = logging.getLogger(__name__)

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

class _JabsorbRpcServlet(SimpleJSONRPCDispatcher):
    """
    A JSON-RPC servlet, replacing the SimpleJSONRPCDispatcher from jsonrpclib,
    converting data from and to Jabsorb format.
    """
    def __init__(self, dispatch_method, encoding=None):
        """
        Sets up the servlet
        """
        SimpleJSONRPCDispatcher.__init__(self, encoding)

        # Register the system.* functions
        self.register_introspection_functions()

        # Make a link to the dispatch method
        self._dispatch_method = dispatch_method

    def _simple_dispatch(self, name, params):
        """
        Dispatch method
        """
        try:
            # Internal method
            return self.funcs[name](*params)

        except KeyError:
            # Other method
            pass

        # Avoid calling this method in the "except" block, as it would be in
        # an exception state (logs will consider the KeyError as a failure)
        return self._dispatch_method(name, params)

    def do_POST(self, request, response):
        """
        Handle a post request

        :param request: The HTTP request bean
        :param request: The HTTP response handler
        """
        # Get the request JSON content
        data = jsonrpclib.loads(to_str(request.read_data()))

        # Convert from Jabsorb
        data = jabsorb.from_jabsorb(data)

        # Dispatch
        try:
            result = self._unmarshaled_dispatch(data, self._simple_dispatch)

        except NoMulticallResult:
            # No result (never happens, but who knows...)
            result = None

        if result is not None:
            # Convert result to Jabsorb
            if 'result' in result:
                result['result'] = jabsorb.to_jabsorb(result['result'])

            # Store JSON
            result = jsonrpclib.jdumps(result)

        else:
            # It was a notification
            result = ''

        # Send the result
        response.send_content(200, result, 'application/json-rpc')

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

@ComponentFactory("cohorte-jabsorbrpc-exporter-factory")
@Provides(pelix.remote.SERVICE_EXPORT_PROVIDER)
@Requires('_dispatcher', pelix.remote.SERVICE_DISPATCHER)
@Requires('_http', pelix.http.HTTP_SERVICE)
@Property('_path', pelix.http.HTTP_SERVLET_PATH, HOST_SERVLET_PATH)
@Property('_kinds', pelix.remote.PROP_REMOTE_CONFIGS_SUPPORTED,
          (JABSORB_CONFIG, 'jabsorb-rpc'))
class JabsorbRpcServiceExporter(object):
    """
    JABSORB-RPC Remote Services exporter
    """
    def __init__(self):
        """
        Sets up the exporter
        """
        # Bundle context
        self._context = None

        # Dispatcher
        self._dispatcher = None

        # Supported configurations
        self._kinds = None

        # HTTP Service
        self._http = None
        self._path = None

        # JSON-RPC servlet
        self._servlet = None

        # Exported services: Name -> ExportEndpoint
        self.__endpoints = {}

        # Thread safety
        self.__lock = threading.Lock()

    def _dispatch(self, method, params):
        """
        Called by the JSON-RPC servlet: calls the method of an exported service
        """
        # Get the best matching name
        matching = None
        len_found = 0
        for name in self.__endpoints:
            if len(name) > len_found and method.startswith(name + "."):
                # Better matching end point name (longer that previous one)
                matching = name
                len_found = len(matching)

        if matching is None:
            # No end point name match
            raise KeyError("No end point found for: {0}".format(method))

        # Extract the method name. (+1 for the trailing dot)
        method_name = method[len_found + 1:]

        # Get the service
        try:
            service = self.__endpoints[matching].instance
        except KeyError:
            raise RemoteServiceError("Unknown endpoint: {0}".format(matching))

        # Get the method
        method_ref = getattr(service, method_name, None)
        if method_ref is None:
            raise RemoteServiceError("Unknown method {0}".format(method))

        # Call it (let the errors be propagated)
        return method_ref(*params)

    def handles(self, configurations):
        """
        Checks if this provider handles the given configuration types

        :param configurations: Configuration types
        """
        if configurations is None or configurations == '*':
            # 'Matches all'
            return True

        return bool(set(configurations).intersection(self._kinds))

    def export_service(self, svc_ref, name, fw_uid):
        """
        Prepares an export endpoint

        :param svc_ref: Service reference
        :param name: Endpoint name
        :param fw_uid: Framework UID
        :return: An ExportEndpoint bean
        :raise NameError: Already known name
        :raise BundleException: Error getting the service
        """
        jabsorb_name = svc_ref.get_property(PROP_ENDPOINT_NAME)
        if jabsorb_name:
            # The end point name has been configured in the Jabsorb way
            name = jabsorb_name

        with self.__lock:
            if name in self.__endpoints:
                # Already known end point
                raise NameError("Already known end point %s for JABSORB-RPC",
                                name)

            # Get the service (let it raise a BundleException if any)
            service = self._context.get_service(svc_ref)

            # Prepare extra properties
            properties = {PROP_ENDPOINT_NAME: name}

            # FIXME: setup HTTP accesses
            # Comma-separated string
            properties[PROP_HTTP_ACCESSES] = self.get_access()

            # Prepare the export endpoint
            try:
                endpoint = pelix.remote.beans.ExportEndpoint(str(uuid.uuid4()),
                                                             fw_uid,
                                                             self._kinds,
                                                             name, svc_ref,
                                                             service,
                                                             properties)
            except ValueError:
                # No specification to export (specifications filtered, ...)
                return None

            # Store information
            self.__endpoints[name] = endpoint

            # Return the endpoint bean
            return endpoint

    def update_export(self, endpoint, new_name, old_properties):
        """
        Updates an export endpoint

        :param endpoint: An ExportEndpoint bean
        :param new_name: Future endpoint name
        :param old_properties: Previous properties
        :raise NameError: Rename refused
        """
        with self.__lock:
            if new_name in self.__endpoints:
                # Reject the new name
                raise NameError("New name of %s already used: %s",
                                endpoint.name, new_name)

            # Update storage
            self.__endpoints[new_name] = self.__endpoints.pop(endpoint.name)

            # Update the endpoint
            endpoint.name = new_name

    def unexport_service(self, endpoint):
        """
        Deletes an export endpoint

        :param endpoint: An ExportEndpoint bean
        """
        with self.__lock:
            try:
                # Clean up storage
                del self.__endpoints[endpoint.name]

            except KeyError:
                # Unknown endpoint
                _logger.warning("Unknown endpoint: %s", endpoint)

            else:
                # Release the service
                svc_ref = endpoint.reference
                self._context.unget_service(svc_ref)

    def get_access(self):
        """
        Retrieves the URL to access this component
        """
        host, port = self._http.get_access()
        if ':' in host:
            # IPv6 address
            host = '[{0}]'.format(host)

        return "http://{0}:{1}{2}".format(host, port, self._path)

    @Validate
    def validate(self, context):
        """
        Component validated
        """
        # Store the context
        self._context = context

        # Create/register the servlet
        self._servlet = _JabsorbRpcServlet(self._dispatch)
        self._http.register_servlet(self._path, self._servlet)

    @Invalidate
    def invalidate(self, context):
        """
        Component invalidated
        """
        # Unregister the servlet
        self._http.unregister(None, self._servlet)

        # Clean up the storage
        self.__endpoints.clear()

        # Clean up members
        self._servlet = None
        self._context = None

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

class _ServiceCallProxy(object):
    """
    Service call proxy
    """
    def __init__(self, uid, name, url, on_error):
        """
        Sets up the call proxy

        :param uid: End point UID
        :param name: End point name
        :param url: End point URL
        :param on_error: A method to call back in case of socket error
        """
        self.__uid = uid
        self.__name = name
        self.__url = url
        self.__on_error = on_error

    def __getattr__(self, name):
        """
        Prefixes the requested attribute name by the endpoint name
        """
        # Make a proxy for this call
        # This is an ugly trick to handle multithreaded calls, as the underlying
        # proxy re-uses the same connection when possible: sometimes it means
        # sending a request before retrieving a result
        proxy = jsonrpclib.ServerProxy(self.__url)

        def wrapped_call(*args, **kwargs):
            """
            Wrapped call
            """
            # Get the method from the proxy
            method = getattr(proxy, "{0}.{1}".format(self.__name, name))

            # Convert arguments
            args = [jabsorb.to_jabsorb(arg) for arg in args]
            kwargs = dict([(key, jabsorb.to_jabsorb(value))
                               for key, value in kwargs.items()])

            try:
                result = method(*args, **kwargs)
                return jabsorb.from_jabsorb(result)

            except socket.error:
                # In case of transport error, look if the service has gone away
                if self.__on_error is not None:
                    self.__on_error(self.__uid)

                # Let the exception stop the caller
                raise

        return wrapped_call

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

@ComponentFactory("cohorte-jabsorbrpc-importer-factory")
@Provides(pelix.remote.SERVICE_IMPORT_ENDPOINT_LISTENER)
@Property('_kinds', pelix.remote.PROP_REMOTE_CONFIGS_SUPPORTED,
          (JABSORB_CONFIG, 'jabsorb-rpc'))
class JabsorbRpcServiceImporter(object):
    """
    JABSORB-RPC Remote Services importer
    """
    def __init__(self):
        """
        Sets up the exporter
        """
        # Bundle context
        self._context = None

        # Component properties
        self._kinds = None

        # Registered services (endpoint UID -> ServiceReference)
        self.__registrations = {}
        self.__reg_lock = threading.Lock()

    def endpoint_added(self, endpoint):
        """
        An end point has been imported
        """
        configs = set(endpoint.configurations)
        if '*' not in configs and not configs.intersection(self._kinds):
            # Not for us
            return

        # Get the access URL
        access_url = endpoint.properties.get(PROP_HTTP_ACCESSES)
        if not access_url:
            # No URL information
            _logger.warning("No access URL given: %s", endpoint)
            return

        else:
            # Get the first URL in the list
            access_url = access_url.split(',')[0]

        _logger.debug("Chosen access: %s", access_url)

        with self.__reg_lock:
            # Already known end point
            if endpoint.uid in self.__registrations:
                return

            # Compute the name
            name = endpoint.properties.get(PROP_ENDPOINT_NAME)
            if not name:
                _logger.error("Remote endpoint has no name: %s", endpoint)
                return

            _logger.debug("Importing %s with name = %s", endpoint, name)

            # Register the service
            svc = _ServiceCallProxy(endpoint.uid, name, access_url,
                                    self._unregister)
            svc_reg = self._context.register_service(endpoint.specifications,
                                                     svc, endpoint.properties)

            # Store references
            self.__registrations[endpoint.uid] = svc_reg

    def endpoint_updated(self, endpoint, old_properties):
        """
        An end point has been updated
        """
        with self.__reg_lock:
            try:
                # Update service properties
                svc_reg = self.__registrations[endpoint.uid]
                svc_reg.set_properties(endpoint.properties)

            except KeyError:
                # Unknown end point
                return

    def endpoint_removed(self, endpoint):
        """
        An end point has been removed
        """
        with self.__reg_lock:
            if endpoint.uid in self.__registrations:
                # Unregister the end point
                self._unregister(endpoint.uid)

    def _unregister(self, endpoint_uid):
        """
        Unregisters the service associated to the given UID

        :param endpoint_uid: An end point UID
        :return: True on success, else False
        """
        try:
            # Pop references
            svc_reg = self.__registrations.pop(endpoint_uid)

            # Unregister the service
            svc_reg.unregister()
            return True

        except KeyError:
            _logger.debug("Unknown end point %s", endpoint_uid)
            return False

        except pelix.framework.BundleException as ex:
            _logger.debug("Can't unregister end point %s: %s", endpoint_uid, ex)
            return False

    @Validate
    def validate(self, context):
        """
        Component validated
        """
        self._context = context

    @Invalidate
    def invalidate(self, context):
        """
        Component invalidated
        """
        self._context = None
<commit_msg>Added 'server' variable replacement in Python Jabsorb RPC<commit_after>#!/usr/bin/env python
# -- Content-Encoding: UTF-8 --
"""
COHORTE Remote Services: JABSORB-RPC, i.e. JSON-RPC in Jabsorb format

:author: Thomas Calmant
:copyright: Copyright 2013, isandlaTech
:license: Apache License 2.0
:version: 1.1
:status: Beta

..

    Copyright 2013 isandlaTech

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

# Documentation strings format
__docformat__ = "restructuredtext en"

# Module version
__version_info__ = (1, 1, 0)
__version__ = ".".join(str(x) for x in __version_info__)

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

# JSON-RPC
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCDispatcher, \
    NoMulticallResult
import jsonrpclib.jsonrpc as jsonrpclib

# Cohorte
import cohorte.java.jabsorb as jabsorb

# iPOPO Decorators
from pelix.ipopo.decorators import ComponentFactory, Provides, Validate, \
    Invalidate, Requires, Property
import pelix.framework
import pelix.http
import pelix.remote.beans
from pelix.remote import RemoteServiceError
from pelix.utilities import to_str

# Standard library
import logging
import socket
import threading
import uuid

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

JABSORB_CONFIG = 'ecf.jabsorb'
""" Remote Service configuration constant """

PROP_ENDPOINT_NAME = '{0}.name'.format(JABSORB_CONFIG)
""" Name of the endpoint """

PROP_HTTP_ACCESSES = '{0}.accesses'.format(JABSORB_CONFIG)
""" HTTP accesses (comma-separated String) """

HOST_SERVLET_PATH = "/JABSORB-RPC"
""" Default servlet path """

_logger = logging.getLogger(__name__)

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

class _JabsorbRpcServlet(SimpleJSONRPCDispatcher):
    """
    A JSON-RPC servlet, replacing the SimpleJSONRPCDispatcher from jsonrpclib,
    converting data from and to Jabsorb format.
    """
    def __init__(self, dispatch_method, encoding=None):
        """
        Sets up the servlet
        """
        SimpleJSONRPCDispatcher.__init__(self, encoding)

        # Register the system.* functions
        self.register_introspection_functions()

        # Make a link to the dispatch method
        self._dispatch_method = dispatch_method

    def _simple_dispatch(self, name, params):
        """
        Dispatch method
        """
        try:
            # Internal method
            return self.funcs[name](*params)

        except KeyError:
            # Other method
            pass

        # Avoid calling this method in the "except" block, as it would be in
        # an exception state (logs will consider the KeyError as a failure)
        return self._dispatch_method(name, params)

    def do_POST(self, request, response):
        """
        Handle a post request

        :param request: The HTTP request bean
        :param request: The HTTP response handler
        """
        # Get the request JSON content
        data = jsonrpclib.loads(to_str(request.read_data()))

        # Convert from Jabsorb
        data = jabsorb.from_jabsorb(data)

        # Dispatch
        try:
            result = self._unmarshaled_dispatch(data, self._simple_dispatch)

        except NoMulticallResult:
            # No result (never happens, but who knows...)
            result = None

        if result is not None:
            # Convert result to Jabsorb
            if 'result' in result:
                result['result'] = jabsorb.to_jabsorb(result['result'])

            # Store JSON
            result = jsonrpclib.jdumps(result)

        else:
            # It was a notification
            result = ''

        # Send the result
        response.send_content(200, result, 'application/json-rpc')

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

@ComponentFactory("cohorte-jabsorbrpc-exporter-factory")
@Provides(pelix.remote.SERVICE_EXPORT_PROVIDER)
@Requires('_dispatcher', pelix.remote.SERVICE_DISPATCHER)
@Requires('_http', pelix.http.HTTP_SERVICE)
@Property('_path', pelix.http.HTTP_SERVLET_PATH, HOST_SERVLET_PATH)
@Property('_kinds', pelix.remote.PROP_REMOTE_CONFIGS_SUPPORTED,
          (JABSORB_CONFIG, 'jabsorb-rpc'))
class JabsorbRpcServiceExporter(object):
    """
    JABSORB-RPC Remote Services exporter
    """
    def __init__(self):
        """
        Sets up the exporter
        """
        # Bundle context
        self._context = None

        # Dispatcher
        self._dispatcher = None

        # Supported configurations
        self._kinds = None

        # HTTP Service
        self._http = None
        self._path = None

        # JSON-RPC servlet
        self._servlet = None

        # Exported services: Name -> ExportEndpoint
        self.__endpoints = {}

        # Thread safety
        self.__lock = threading.Lock()

    def _dispatch(self, method, params):
        """
        Called by the JSON-RPC servlet: calls the method of an exported service
        """
        # Get the best matching name
        matching = None
        len_found = 0
        for name in self.__endpoints:
            if len(name) > len_found and method.startswith(name + "."):
                # Better matching end point name (longer that previous one)
                matching = name
                len_found = len(matching)

        if matching is None:
            # No end point name match
            raise KeyError("No end point found for: {0}".format(method))

        # Extract the method name. (+1 for the trailing dot)
        method_name = method[len_found + 1:]

        # Get the service
        try:
            service = self.__endpoints[matching].instance
        except KeyError:
            raise RemoteServiceError("Unknown endpoint: {0}".format(matching))

        # Get the method
        method_ref = getattr(service, method_name, None)
        if method_ref is None:
            raise RemoteServiceError("Unknown method {0}".format(method))

        # Call it (let the errors be propagated)
        return method_ref(*params)

    def handles(self, configurations):
        """
        Checks if this provider handles the given configuration types

        :param configurations: Configuration types
        """
        if configurations is None or configurations == '*':
            # 'Matches all'
            return True

        return bool(set(configurations).intersection(self._kinds))

    def export_service(self, svc_ref, name, fw_uid):
        """
        Prepares an export endpoint

        :param svc_ref: Service reference
        :param name: Endpoint name
        :param fw_uid: Framework UID
        :return: An ExportEndpoint bean
        :raise NameError: Already known name
        :raise BundleException: Error getting the service
        """
        jabsorb_name = svc_ref.get_property(PROP_ENDPOINT_NAME)
        if jabsorb_name:
            # The end point name has been configured in the Jabsorb way
            name = jabsorb_name

        with self.__lock:
            if name in self.__endpoints:
                # Already known end point
                raise NameError("Already known end point %s for JABSORB-RPC",
                                name)

            # Get the service (let it raise a BundleException if any)
            service = self._context.get_service(svc_ref)

            # Prepare extra properties
            properties = {PROP_ENDPOINT_NAME: name}

            # FIXME: setup HTTP accesses
            # Comma-separated string
            properties[PROP_HTTP_ACCESSES] = self.get_access()

            # Prepare the export endpoint
            try:
                endpoint = pelix.remote.beans.ExportEndpoint(str(uuid.uuid4()),
                                                             fw_uid,
                                                             self._kinds,
                                                             name, svc_ref,
                                                             service,
                                                             properties)
            except ValueError:
                # No specification to export (specifications filtered, ...)
                return None

            # Store information
            self.__endpoints[name] = endpoint

            # Return the endpoint bean
            return endpoint

    def update_export(self, endpoint, new_name, old_properties):
        """
        Updates an export endpoint

        :param endpoint: An ExportEndpoint bean
        :param new_name: Future endpoint name
        :param old_properties: Previous properties
        :raise NameError: Rename refused
        """
        with self.__lock:
            if new_name in self.__endpoints:
                # Reject the new name
                raise NameError("New name of %s already used: %s",
                                endpoint.name, new_name)

            # Update storage
            self.__endpoints[new_name] = self.__endpoints.pop(endpoint.name)

            # Update the endpoint
            endpoint.name = new_name

    def unexport_service(self, endpoint):
        """
        Deletes an export endpoint

        :param endpoint: An ExportEndpoint bean
        """
        with self.__lock:
            try:
                # Clean up storage
                del self.__endpoints[endpoint.name]

            except KeyError:
                # Unknown endpoint
                _logger.warning("Unknown endpoint: %s", endpoint)

            else:
                # Release the service
                svc_ref = endpoint.reference
                self._context.unget_service(svc_ref)

    def get_access(self):
        """
        Retrieves the URL to access this component
        """
        host, port = self._http.get_access()
        if ':' in host:
            # IPv6 address
            host = '[{0}]'.format(host)

        return "http://{0}:{1}{2}".format(host, port, self._path)

    @Validate
    def validate(self, context):
        """
        Component validated
        """
        # Store the context
        self._context = context

        # Create/register the servlet
        self._servlet = _JabsorbRpcServlet(self._dispatch)
        self._http.register_servlet(self._path, self._servlet)

    @Invalidate
    def invalidate(self, context):
        """
        Component invalidated
        """
        # Unregister the servlet
        self._http.unregister(None, self._servlet)

        # Clean up the storage
        self.__endpoints.clear()

        # Clean up members
        self._servlet = None
        self._context = None

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

class _ServiceCallProxy(object):
    """
    Service call proxy
    """
    def __init__(self, uid, name, url, on_error):
        """
        Sets up the call proxy

        :param uid: End point UID
        :param name: End point name
        :param url: End point URL
        :param on_error: A method to call back in case of socket error
        """
        self.__uid = uid
        self.__name = name
        self.__url = url
        self.__on_error = on_error

    def __getattr__(self, name):
        """
        Prefixes the requested attribute name by the endpoint name
        """
        # Make a proxy for this call
        # This is an ugly trick to handle multithreaded calls, as the underlying
        # proxy re-uses the same connection when possible: sometimes it means
        # sending a request before retrieving a result
        proxy = jsonrpclib.ServerProxy(self.__url)

        def wrapped_call(*args, **kwargs):
            """
            Wrapped call
            """
            # Get the method from the proxy
            method = getattr(proxy, "{0}.{1}".format(self.__name, name))

            # Convert arguments
            args = [jabsorb.to_jabsorb(arg) for arg in args]
            kwargs = dict([(key, jabsorb.to_jabsorb(value))
                               for key, value in kwargs.items()])

            try:
                result = method(*args, **kwargs)
                return jabsorb.from_jabsorb(result)

            except socket.error:
                # In case of transport error, look if the service has gone away
                if self.__on_error is not None:
                    self.__on_error(self.__uid)

                # Let the exception stop the caller
                raise

        return wrapped_call

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

@ComponentFactory("cohorte-jabsorbrpc-importer-factory")
@Provides(pelix.remote.SERVICE_IMPORT_ENDPOINT_LISTENER)
@Property('_kinds', pelix.remote.PROP_REMOTE_CONFIGS_SUPPORTED,
          (JABSORB_CONFIG, 'jabsorb-rpc'))
class JabsorbRpcServiceImporter(object):
    """
    JABSORB-RPC Remote Services importer
    """
    def __init__(self):
        """
        Sets up the exporter
        """
        # Bundle context
        self._context = None

        # Component properties
        self._kinds = None

        # Registered services (endpoint UID -> ServiceReference)
        self.__registrations = {}
        self.__reg_lock = threading.Lock()

    def endpoint_added(self, endpoint):
        """
        An end point has been imported
        """
        configs = set(endpoint.configurations)
        if '*' not in configs and not configs.intersection(self._kinds):
            # Not for us
            return

        # Get the access URL
        access_url = endpoint.properties.get(PROP_HTTP_ACCESSES)
        if not access_url:
            # No URL information
            _logger.warning("No access URL given: %s", endpoint)
            return

        # Get the first URL in the list
        access_url = access_url.split(',')[0]

        # Replace the server variable
        if endpoint.server:
            access_url = access_url.replace('{server}', endpoint.server)

        _logger.debug("Chosen access: %s", access_url)

        with self.__reg_lock:
            # Already known end point
            if endpoint.uid in self.__registrations:
                return

            # Compute the name
            name = endpoint.properties.get(PROP_ENDPOINT_NAME)
            if not name:
                _logger.error("Remote endpoint has no name: %s", endpoint)
                return

            _logger.debug("Importing %s with name = %s", endpoint, name)

            # Register the service
            svc = _ServiceCallProxy(endpoint.uid, name, access_url,
                                    self._unregister)
            svc_reg = self._context.register_service(endpoint.specifications,
                                                     svc, endpoint.properties)

            # Store references
            self.__registrations[endpoint.uid] = svc_reg

    def endpoint_updated(self, endpoint, old_properties):
        """
        An end point has been updated
        """
        with self.__reg_lock:
            try:
                # Update service properties
                svc_reg = self.__registrations[endpoint.uid]
                svc_reg.set_properties(endpoint.properties)

            except KeyError:
                # Unknown end point
                return

    def endpoint_removed(self, endpoint):
        """
        An end point has been removed
        """
        with self.__reg_lock:
            if endpoint.uid in self.__registrations:
                # Unregister the end point
                self._unregister(endpoint.uid)

    def _unregister(self, endpoint_uid):
        """
        Unregisters the service associated to the given UID

        :param endpoint_uid: An end point UID
        :return: True on success, else False
        """
        try:
            # Pop references
            svc_reg = self.__registrations.pop(endpoint_uid)

            # Unregister the service
            svc_reg.unregister()
            return True

        except KeyError:
            _logger.debug("Unknown end point %s", endpoint_uid)
            return False

        except pelix.framework.BundleException as ex:
            _logger.debug("Can't unregister end point %s: %s", endpoint_uid, ex)
            return False

    @Validate
    def validate(self, context):
        """
        Component validated
        """
        self._context = context

    @Invalidate
    def invalidate(self, context):
        """
        Component invalidated
        """
        self._context = None
<|endoftext|>


<commit_before>import random
import zlib
from numbers import Real
import numpy as np
from Orange.data import DiscreteVariable, Variable, Storage, Value

class Discrete(np.ndarray):
    def __new__(cls, variable, data=None):
        if variable is not None:
            if not isinstance(variable, Variable):
                if hasattr(data, "domain"):
                    variable = data.domain[variable]
                if hasattr(data, "variable"):
                    variable = data.variable
            Discrete._check_var_type(variable)
            n = len(variable.values)
        else:
            if isinstance(data, Storage):
                raise ValueError("variable is not specified")
            n = len(data)

        self = super().__new__(cls, n)
        self.variable = variable
        if isinstance(data, Storage):
            self.compute(variable, data)
        elif data is None:
            self[:] = self.unknowns = 0
        else:
            self[:] = data
            self.unknowns = getattr(data, "unknowns", 0)
        return self

    def __eq__(self, other):
        return np.array_equal(self, other) and (
            not hasattr(other, "unknowns") or self.unknowns == other.unknowns)

    def __getitem__(self, index):
        if isinstance(index, str):
            index = self.variable.to_val(index)
        return super().__getitem__(index)

    def __setitem__(self, index, value):
        if isinstance(index, str):
            index = self.variable.to_val(index)
        super().__setitem__(index, value)

    def __hash__(self):
        return zlib.adler32(self) ^ hash(self.unknowns)

    def __add__(self, other):
        s = super().__add__(other)
        s.unknowns = self.unknowns + getattr(other, "unknowns", 0)
        return s

    def __iadd__(self, other):
        super().__iadd__(other)
        self.unknowns += getattr(other, "unknowns", 0)
        return self

    def __sub__(self, other):
        s = super().__sub__(other)
        s.unknowns = self.unknowns - getattr(other, "unknowns", 0)
        return s

    def __isub__(self, other):
        super().__isub__(other)
        self.unknowns -= getattr(other, "unknowns", 0)
        return self

    def __mul__(self, other):
        s = super().__mul__(other)
        if isinstance(other, Real):
            s.unknowns = self.unknowns / other
        return s

    def __imul__(self, other):
        super().__imul__(other)
        if isinstance(other, Real):
            self.unknowns *= other
        return self

    def __div__(self, other):
        s = super().__mul__(other)
        if isinstance(other, Real):
            s.unknowns = self.unknowns / other
        return s

    def __idiv__(self, other):
        super().__imul__(other)
        if isinstance(other, Real):
            self.unknowns /= other
        return self

    @staticmethod
    def _check_var_type(var):
        if not isinstance(var, DiscreteVariable):
            if isinstance(var, Variable):
                raise ValueError("expected discrete variable not %s" % var)
            else:
                raise ValueError("expected DiscreteVariable, not '%s'" %
                    type(var).__name__)

    def compute(self, variable, data):
        var = data.domain[variable]
        Discrete._check_var_type(var)
        self.variable = variable
        try:
            self[:], self.unknowns = data._compute_distributions([variable])[0]
            return
        except NotImplementedError:
            pass

        self[:] = self.unknowns = 0
        if data.has_weights():
            for val, w in zip(data[:, variable], data.W):
                if val == val:
                    self[val] += w
                else:
                    self.unknowns += w
        else:
            for inst in data:
                val = inst[var]
                if val == val:
                    self[val] += 1
                else:
                    self.unknowns += 1

    def normalize(self):
        self /= np.sum(self)

    def modus(self):
        val = np.argmax(self)
        return Value(self.variable, val) if self.variable is not None else val

    def random(self):
        v = random.random() * np.sum(self)
        s = 0
        for i, e in enumerate(self):
            s += e
            if s > v:
                break
        return Value(self.variable, i) if self.variable is not None else i

def class_distribution(data):
    nattrs = len(data.domain.attributes)
    if data.domain.class_var:
        return Discrete(nattrs, data)
    return [Discrete(nattrs+i, data)
            for i in range(len(data.domain.class_vars))]
<commit_msg>Fixed distribution.Discrete.Normalize<commit_after>import random
import zlib
from numbers import Real
import numpy as np
from Orange.data import DiscreteVariable, Variable, Storage, Value

class Discrete(np.ndarray):
    def __new__(cls, variable, data=None):
        if variable is not None:
            if not isinstance(variable, Variable):
                if hasattr(data, "domain"):
                    variable = data.domain[variable]
                if hasattr(data, "variable"):
                    variable = data.variable
            Discrete._check_var_type(variable)
            n = len(variable.values)
        else:
            if isinstance(data, Storage):
                raise ValueError("variable is not specified")
            n = len(data)

        self = super().__new__(cls, n)
        self.variable = variable
        if isinstance(data, Storage):
            self.compute(variable, data)
        elif data is None:
            self[:] = self.unknowns = 0
        else:
            self[:] = data
            self.unknowns = getattr(data, "unknowns", 0)
        return self

    def __eq__(self, other):
        return np.array_equal(self, other) and (
            not hasattr(other, "unknowns") or self.unknowns == other.unknowns)

    def __getitem__(self, index):
        if isinstance(index, str):
            index = self.variable.to_val(index)
        return super().__getitem__(index)

    def __setitem__(self, index, value):
        if isinstance(index, str):
            index = self.variable.to_val(index)
        super().__setitem__(index, value)

    def __hash__(self):
        return zlib.adler32(self) ^ hash(self.unknowns)

    def __add__(self, other):
        s = super().__add__(other)
        s.unknowns = self.unknowns + getattr(other, "unknowns", 0)
        return s

    def __iadd__(self, other):
        super().__iadd__(other)
        self.unknowns += getattr(other, "unknowns", 0)
        return self

    def __sub__(self, other):
        s = super().__sub__(other)
        s.unknowns = self.unknowns - getattr(other, "unknowns", 0)
        return s

    def __isub__(self, other):
        super().__isub__(other)
        self.unknowns -= getattr(other, "unknowns", 0)
        return self

    def __mul__(self, other):
        s = super().__mul__(other)
        if isinstance(other, Real):
            s.unknowns = self.unknowns / other
        return s

    def __imul__(self, other):
        super().__imul__(other)
        if isinstance(other, Real):
            self.unknowns *= other
        return self

    def __div__(self, other):
        s = super().__mul__(other)
        if isinstance(other, Real):
            s.unknowns = self.unknowns / other
        return s

    def __idiv__(self, other):
        super().__imul__(other)
        if isinstance(other, Real):
            self.unknowns /= other
        return self

    @staticmethod
    def _check_var_type(var):
        if not isinstance(var, DiscreteVariable):
            if isinstance(var, Variable):
                raise ValueError("expected discrete variable not %s" % var)
            else:
                raise ValueError("expected DiscreteVariable, not '%s'" %
                    type(var).__name__)

    def compute(self, variable, data):
        var = data.domain[variable]
        Discrete._check_var_type(var)
        self.variable = variable
        try:
            self[:], self.unknowns = data._compute_distributions([variable])[0]
            return
        except NotImplementedError:
            pass

        self[:] = self.unknowns = 0
        if data.has_weights():
            for val, w in zip(data[:, variable], data.W):
                if val == val:
                    self[val] += w
                else:
                    self.unknowns += w
        else:
            for inst in data:
                val = inst[var]
                if val == val:
                    self[val] += 1
                else:
                    self.unknowns += 1

    def normalize(self):
        t = np.sum(self)
        self[:] /= t
        np.unknowns /= t

    def modus(self):
        val = np.argmax(self)
        return Value(self.variable, val) if self.variable is not None else val

    def random(self):
        v = random.random() * np.sum(self)
        s = 0
        for i, e in enumerate(self):
            s += e
            if s > v:
                break
        return Value(self.variable, i) if self.variable is not None else i

def class_distribution(data):
    nattrs = len(data.domain.attributes)
    if data.domain.class_var:
        return Discrete(nattrs, data)
    return [Discrete(nattrs+i, data)
            for i in range(len(data.domain.class_vars))]
<|endoftext|>


<commit_before>"""Tests for the rate network."""

import time
import numpy as np
from fake_spectra import rate_network
from fake_spectra import ratenetworkspectra
from fake_spectra._spectra_priv import _interpolate_2d

def _exact_alphaHp():
    """For hydrogen recombination we have an exact answer from Ferland et al 1992 (http://adsabs.harvard.edu/abs/1992ApJ...387...95F).
    This function returns as an array these rates, for testing purposes."""
    #case B recombination rates for hydrogen from Ferland 92, final column of Table 1. For n >= 2.
    f92g2 = np.array([5.758e-11, 2.909e-11, 1.440e-11, 6.971e-12,3.282e-12, 1.489e-12, 6.43e-13, 2.588e-13, 9.456e-14, 3.069e-14, 8.793e-15, 2.245e-15, 5.190e-16, 1.107e-16, 2.221e-17, 4.267e-18, 7.960e-19, 1.457e-19,2.636e-20, 4.737e-21])
    #case B recombination rates for hydrogen from Ferland 92, second column of Table 1. For n == 1.
    f92n1 = np.array([9.258e-12, 5.206e-12, 2.927e-12, 1.646e-12, 9.246e-13, 5.184e-13, 2.890e-13, 1.582e-13, 8.255e-14, 3.882e-14, 1.545e-14, 5.058e-15, 1.383e-15, 3.276e-16, 7.006e-17, 1.398e-17, 2.665e-18, 4.940e-19, 9.001e-20, 1.623e-20])
    tt = 10**np.linspace(0.5, 10, 20)
    return (tt, f92g2+f92n1)

def testRecombRates():
    """Test the recombination rates"""
    (tt, recomb_exact) = _exact_alphaHp()
    recomb = rate_network.RecombRatesVerner96()
    assert np.all(np.abs(recomb.alphaHp(tt)/ recomb_exact-1.) < 1e-2)
    #Cen rates are not very accurate.
    recomb = rate_network.RecombRatesCen92()
    assert np.all(np.abs(recomb.alphaHp(tt[4:12])/ recomb_exact[4:12]-1.) < 0.5)

def testRateNetwork():
    """Simple tests for the rate network"""
    rates = rate_network.RateNetwork(redshift=2.)
    #Complete ionisation at low density
    assert np.abs(rates.get_equilib_ne(1e-6, 200.,helium=0.24) / (1e-6*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 3e-5
    assert np.abs(rates.get_equilib_ne(1e-6, 200.,helium=0.12) / (1e-6*0.88) - (1 + 2* 0.12/(1-0.12)/4)) < 3e-5
    assert np.abs(rates.get_equilib_ne(1e-5, 200.,helium=0.24) / (1e-5*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 3e-4
    assert np.abs(rates.get_equilib_ne(1e-4, 200.,helium=0.24) / (1e-4*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 2e-3

    assert 9500 < rates.get_temp(1e-4, 200.,helium=0.24) < 9510
    #Roughly prop to internal energy when ionised
    assert np.abs(rates.get_temp(1e-4, 400.,helium=0.24) / rates.get_temp(1e-4, 200.,helium=0.24) - 2.) < 1e-3
    #Neutral fraction prop to density.
    for dens in (1e-4, 1e-5, 1e-6):
        assert np.abs(rates.get_neutral_fraction(dens, 200.,helium=0.24) / dens - 0.3113) < 1e-3
    #Neutral (self-shielded) at high density:
    assert rates.get_neutral_fraction(1, 100.,helium=0.24) > 0.95
    assert 0.75 > rates.get_neutral_fraction(0.1, 100.,helium=0.24) > 0.739

    #Check self-shielding is working.
    rates2 = rate_network.RateNetwork(redshift=2., selfshield=False)
    assert rates2.get_neutral_fraction(1, 100.,helium=0.24) < 0.25
    assert rates2.get_neutral_fraction(0.1, 100.,helium=0.24) <0.05

    #Higher temp for same int. energy.
    assert 14500 < rates.get_temp(1, 200.,helium=0.24) < 14900

    #Check self-shielding is working.
    rates2 = rate_network.RateNetwork(redshift=2., selfshield=False)
    dens = np.logspace(-6,1,50)
    neuts = rates.get_neutral_fraction(dens, 100.)/(0.76*dens)
    neuts2 = rates2.get_neutral_fraction(dens, 100.)/(0.76*dens)
    ii = np.where(dens < 1e-4)
    assert np.all(np.abs(neuts[ii]/neuts2[ii] -1.) < 2e-2)
    before = time.clock()
    ntests = 100
    for i in range(ntests):
        nH_bench = ((1.*i)/ntests-1e-6)
        for j in range(ntests):
            uu_bench = ((2.e5*j)/ntests + 200.)
            rates2.get_temp(nH_bench, uu_bench)
    after = time.clock()
    print(after-before)

def testRateNetworkGas():
    """Test that the spline is working."""
    gasprop = ratenetworkspectra.RateNetworkGas(3, None)
    dlim = (np.log(1e-7), np.log(3))
    elim = (np.log(20), np.log(3e6))
    randd = (dlim[1] - dlim[0]) * np.random.random(size=2000) + dlim[0]
    randi = (elim[1] - elim[0]) * np.random.random(size=2000) + elim[0]
    gasprop.build_interp(dlim, elim)
    spl = _interpolate_2d(randd.astype(np.float32), randi.astype(np.float32), gasprop.densgrid, gasprop.ienergygrid, gasprop.lh0grid)
    rate = np.array([np.log(gasprop.rates.get_neutral_fraction(np.exp(dd), np.exp(ii))) for dd, ii in zip(randd, randi)])
    assert np.all(np.abs(spl - rate) < 1e-5)
<commit_msg>Fix tests<commit_after>"""Tests for the rate network."""

import time
import numpy as np
from fake_spectra import rate_network
from fake_spectra import ratenetworkspectra
from fake_spectra._spectra_priv import _interpolate_2d

def _exact_alphaHp():
    """For hydrogen recombination we have an exact answer from Ferland et al 1992 (http://adsabs.harvard.edu/abs/1992ApJ...387...95F).
    This function returns as an array these rates, for testing purposes."""
    #case B recombination rates for hydrogen from Ferland 92, final column of Table 1. For n >= 2.
    f92g2 = np.array([5.758e-11, 2.909e-11, 1.440e-11, 6.971e-12,3.282e-12, 1.489e-12, 6.43e-13, 2.588e-13, 9.456e-14, 3.069e-14, 8.793e-15, 2.245e-15, 5.190e-16, 1.107e-16, 2.221e-17, 4.267e-18, 7.960e-19, 1.457e-19,2.636e-20, 4.737e-21])
    #case B recombination rates for hydrogen from Ferland 92, second column of Table 1. For n == 1.
    f92n1 = np.array([9.258e-12, 5.206e-12, 2.927e-12, 1.646e-12, 9.246e-13, 5.184e-13, 2.890e-13, 1.582e-13, 8.255e-14, 3.882e-14, 1.545e-14, 5.058e-15, 1.383e-15, 3.276e-16, 7.006e-17, 1.398e-17, 2.665e-18, 4.940e-19, 9.001e-20, 1.623e-20])
    tt = 10**np.linspace(0.5, 10, 20)
    return (tt, f92g2+f92n1)

def testRecombRates():
    """Test the recombination rates"""
    (tt, recomb_exact) = _exact_alphaHp()
    recomb = rate_network.RecombRatesVerner96()
    assert np.all(np.abs(recomb.alphaHp(tt)/ recomb_exact-1.) < 1e-2)
    #Cen rates are not very accurate.
    recomb = rate_network.RecombRatesCen92()
    assert np.all(np.abs(recomb.alphaHp(tt[4:12])/ recomb_exact[4:12]-1.) < 0.5)

def testRateNetwork():
    """Simple tests for the rate network"""
    rates = rate_network.RateNetwork(redshift=2.)
    #Complete ionisation at low density
    assert np.abs(rates.get_equilib_ne(1e-6, 200.,helium=0.24) / (1e-6*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 3e-5
    assert np.abs(rates.get_equilib_ne(1e-6, 200.,helium=0.12) / (1e-6*0.88) - (1 + 2* 0.12/(1-0.12)/4)) < 3e-5
    assert np.abs(rates.get_equilib_ne(1e-5, 200.,helium=0.24) / (1e-5*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 3e-4
    assert np.abs(rates.get_equilib_ne(1e-4, 200.,helium=0.24) / (1e-4*0.76) - (1 + 2* 0.24/(1-0.24)/4)) < 2e-3

    assert 9500 < rates.get_temp(1e-4, 200.,helium=0.24) < 9510
    #Roughly prop to internal energy when ionised
    assert np.abs(rates.get_temp(1e-4, 400.,helium=0.24) / rates.get_temp(1e-4, 200.,helium=0.24) - 2.) < 1e-3
    #Neutral fraction prop to density.
    for dens in (1e-4, 1e-5, 1e-6):
        assert np.abs(rates.get_neutral_fraction(dens, 200.,helium=0.24) / dens - 0.3113) < 1e-3
    #Neutral (self-shielded) at high density:
    assert rates.get_neutral_fraction(1, 100.,helium=0.24) > 0.95
    assert 0.75 > rates.get_neutral_fraction(0.1, 100.,helium=0.24) > 0.735

    #Check self-shielding is working.
    rates2 = rate_network.RateNetwork(redshift=2., selfshield=False)
    assert rates2.get_neutral_fraction(1, 100.,helium=0.24) < 0.25
    assert rates2.get_neutral_fraction(0.1, 100.,helium=0.24) <0.05

    #Higher temp for same int. energy.
    assert 14500 < rates.get_temp(1, 200.,helium=0.24) < 14900

    #Check self-shielding is working.
    rates2 = rate_network.RateNetwork(redshift=2., selfshield=False)
    dens = np.logspace(-6,1,50)
    neuts = rates.get_neutral_fraction(dens, 100.)/(0.76*dens)
    neuts2 = rates2.get_neutral_fraction(dens, 100.)/(0.76*dens)
    ii = np.where(dens < 1e-4)
    assert np.all(np.abs(neuts[ii]/neuts2[ii] -1.) < 2e-2)
    before = time.clock()
    ntests = 100
    for i in range(ntests):
        nH_bench = ((1.*i)/ntests-1e-6)
        for j in range(ntests):
            uu_bench = ((2.e5*j)/ntests + 200.)
            rates2.get_temp(nH_bench, uu_bench)
    after = time.clock()
    print(after-before)

def testRateNetworkGas():
    """Test that the spline is working."""
    gasprop = ratenetworkspectra.RateNetworkGas(3, None)
    dlim = (np.log(1e-7), np.log(3))
    elim = (np.log(20), np.log(3e6))
    randd = (dlim[1] - dlim[0]) * np.random.random(size=2000) + dlim[0]
    randi = (elim[1] - elim[0]) * np.random.random(size=2000) + elim[0]
    gasprop.build_interp(dlim, elim)
    spl = _interpolate_2d(randd.astype(np.float32), randi.astype(np.float32), gasprop.densgrid, gasprop.ienergygrid, gasprop.lh0grid)
    rate = np.array([np.log(gasprop.rates.get_neutral_fraction(np.exp(dd), np.exp(ii))) for dd, ii in zip(randd, randi)])
    assert np.all(np.abs(spl - rate) < 1e-5)
<|endoftext|>


<commit_before># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------
"""
Track the modification date for pages.
"""

from django.db import models
from django.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _

def pre_save_handler(sender, instance, **kwargs):
    """
    Intercept attempts to save and insert the current date and time into
    creation and modification date fields.
    """
    from datetime import datetime

    now = datetime.now()
    if instance.id is None:
        instance.creation_date = now
    instance.modification_date = now

# ------------------------------------------------------------------------
def dt_to_utc_timestamp(dt):
    from time import mktime, gmtime
    return mktime(gmtime(mktime(dt.utctimetuple())))

def dt_to_utc_dt(dt):
    from datetime import datetime
    utc_ts = dt_to_utc_timestamp(dt)
    utc_dt = datetime.utcfromtimestamp(utc_ts)
    return utc_dt

def register(cls, admin_cls):
    cls.add_to_class('creation_date',     models.DateTimeField(_('creation date'),     null=True, editable=False))
    cls.add_to_class('modification_date', models.DateTimeField(_('modification date'), null=True, editable=False))

    if hasattr(cls, 'cache_key_components'):
        cls.cache_key_components.append(lambda page: page.modification_date and page.modification_date.strftime('%s'))

    if hasattr(cls, 'last_modified'):
        cls.last_modified = lambda p: dt_to_utc_dt(p.modification_date)

    pre_save.connect(pre_save_handler, sender=cls)

# ------------------------------------------------------------------------
def last_modified_response_processor(self, request, response):
    from django.utils.http import http_date

    # Don't modify header if it's already set
    if response.has_header('Last-Modified'):
        return

    # Don't include Last-Modified if we don't want to be cached
    if response.has_header('Cache-Control') and "no-cache" in response['Cache-Control']:
        return

    response['Last-Modified'] = http_date(dt_to_utc_timestamp(self.modification_date))

# ------------------------------------------------------------------------
<commit_msg>Improve the changedate.last_modified_response_processor.<commit_after># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------
"""
Track the modification date for pages.
"""

from django.db import models
from django.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _

def pre_save_handler(sender, instance, **kwargs):
    """
    Intercept attempts to save and insert the current date and time into
    creation and modification date fields.
    """
    from datetime import datetime

    now = datetime.now()
    if instance.id is None:
        instance.creation_date = now
    instance.modification_date = now

# ------------------------------------------------------------------------
def dt_to_utc_timestamp(dt):
    from time import mktime, gmtime
    return mktime(gmtime(mktime(dt.utctimetuple())))

def dt_to_utc_dt(dt):
    from datetime import datetime
    utc_ts = dt_to_utc_timestamp(dt)
    utc_dt = datetime.utcfromtimestamp(utc_ts)
    return utc_dt

def register(cls, admin_cls):
    cls.add_to_class('creation_date',     models.DateTimeField(_('creation date'),     null=True, editable=False))
    cls.add_to_class('modification_date', models.DateTimeField(_('modification date'), null=True, editable=False))

    if hasattr(cls, 'cache_key_components'):
        cls.cache_key_components.append(lambda page: page.modification_date and page.modification_date.strftime('%s'))

    if hasattr(cls, 'last_modified'):
        cls.last_modified = lambda p: dt_to_utc_dt(p.modification_date)

    pre_save.connect(pre_save_handler, sender=cls)

# ------------------------------------------------------------------------
def last_modified_response_processor(self, request, response):
    from django.utils.http import http_date

    # Don't include Last-Modified if we don't want to be cached
    if "no-cache" in response.get('Cache-Control', ''):
        return

    # If we already have a Last-Modified, take the later one
    from email.utils import parsedate_tz, mktime_tz

    last_modified = [ dt_to_utc_timestamp(self.modification_date) ]
    if response.has_header('Last-Modified'):
        last_modified.append(mktime_tz(parsedate_tz(response['Last-Modified'])))

    response['Last-Modified'] = http_date(max(last_modified))

# ------------------------------------------------------------------------
<|endoftext|>


<commit_before>"""
Migration that addresses the problem reported in GH#3006
On core.migrations.0061, a couple of settings got switched from the general
group to the styling group. The codebase was also change to lookup the settings
using the styling group.
However, the journal_defaults.json was never changed, which has lead to 2
possible wrong states.

Case A: pre-existing installation ran migration core.0061:
    - In this state the settings where swapped correctly to styling. however,
      due to the setting group not being updated, a duplicate setting with the
      group general was re-introduced. This setting will not have any relevant
      values attached, since the codebase has been using the correct setting
      since
    - Action: Delete the setting associated with the general group
Case B: New installations since core.0061 was introduced:
    - In this state, migration 0061 would have run before the journal_defaults
      would've have been loaded. As a result, the styling group exists but
      the settings have always been on the general group. It is safe to delete
      these settings, because the codebase would have never allowed their values
      to be changed.
    - Action: Swap the group setting of the existing setting
      (essentially run 0061 again)

"""
from __future__ import unicode_literals

from django.db import migrations, models

STYLING_SETTINGS = [
    "enable_editorial_images",
    "multi_page_editorial",
]

def update_settings_group(apps, setting_names, from_group, to_group):
    Setting = apps.get_model("core", "Setting")
    SettingGroup = apps.get_model("core", "SettingGroup")
    setting_group, _ = SettingGroup.objects.get_or_create(name=to_group)
    settings = Setting.objects.filter(
        name__in=STYLING_SETTINGS, group__name=from_group,
    )
    settings_with_from = settings.filter(group__name=from_group)
    settings_with_to = settings.filter(group__name=to_group)
    if settings_with_from.exists() and settings_with_to.exists():
        # Case A
        settings_with_from.delete()
    elif settings_with_from.exists() and not settings_with_to.exists():
        # Case B
        setting = settings_with_from.first()
        setting.group = setting_group
        setting.save()
        settings_with_from.delete()

def general_to_styling(apps, schema_editor):
    update_settings_group(apps, STYLING_SETTINGS, "general", "styling")

class Migration(migrations.Migration):

    dependencies = [
        ('core', '0073_auto_20220630_1608'),
    ]

    operations = [
        migrations.RunPython(
            general_to_styling, reverse_code=migrations.RunPython.noop),
    ]
<commit_msg>Fixes bad migration<commit_after>"""
Migration that addresses the problem reported in GH#3006
On core.migrations.0061, a couple of settings got switched from the general
group to the styling group. The codebase was also change to lookup the settings
using the styling group.
However, the journal_defaults.json was never changed, which has lead to 2
possible wrong states.

Case A: pre-existing installation ran migration core.0061:
    - In this state the settings where swapped correctly to styling. however,
      due to the setting group not being updated, a duplicate setting with the
      group general was re-introduced. This setting will not have any relevant
      values attached, since the codebase has been using the correct setting
      since
    - Action: Delete the setting associated with the general group
Case B: New installations since core.0061 was introduced:
    - In this state, migration 0061 would have run before the journal_defaults
      would've have been loaded. As a result, the styling group exists but
      the settings have always been on the general group. It is safe to delete
      these settings, because the codebase would have never allowed their values
      to be changed.
    - Action: Swap the group setting of the existing setting
      (essentially run 0061 again)

"""
from __future__ import unicode_literals

from django.db import migrations, models

STYLING_SETTINGS = [
    "enable_editorial_images",
    "multi_page_editorial",
]

def update_settings_group(apps, setting_names, from_group, to_group):
    Setting = apps.get_model("core", "Setting")
    SettingGroup = apps.get_model("core", "SettingGroup")
    setting_group, _ = SettingGroup.objects.get_or_create(name=to_group)
    for setting_name in STYLING_SETTINGS:
        import pdb;pdb.set_trace()
        settings = Setting.objects.filter(
            name=setting_name,
        )
        settings_with_from = settings.filter(group__name=from_group)
        settings_with_to = settings.filter(group__name=to_group)
        if settings_with_from.exists() and settings_with_to.exists():
            # Case A
            settings_with_from.delete()
        elif settings_with_from.exists() and not settings_with_to.exists():
            # Case B
            setting = settings_with_from.first()
            setting.group = setting_group
            setting.save()
            settings_with_from.delete()

def general_to_styling(apps, schema_editor):
    update_settings_group(apps, STYLING_SETTINGS, "general", "styling")

class Migration(migrations.Migration):

    dependencies = [
        ('core', '0073_auto_20220630_1608'),
    ]

    operations = [
        migrations.RunPython(
            general_to_styling, reverse_code=migrations.RunPython.noop),
    ]
<|endoftext|>


<commit_before>#solarnmf_plotting.py

#Will Barnes
#3 April 2015

import logging
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
from scipy.ndimage.interpolation import rotate

class MakeBSSPlots(object):

    def __init__(self,toption,input_type,u,v,A,T,div=None,q=None,angle=0.0,fig_size=(8,8),print_format='eps',print_dpi=1000,**kwargs):
        self.toption = toption
        self.input_type = input_type
        self.u = u
        self.v = v
        self.A = A
        self.T = T

        if div:
            self.div
        if not q:
            self.q = self.u.shape[1]
        else:
            self.q = q

        self.ny,self.nx = np.shape(T)

        #set optional member variables
        self.angle = angle
        self.print_format = print_format
        self.print_dpi = print_dpi
        self.fs = 18
        self.cm = 'Blues'
        self.zero_tol = 1.e-5
        self.fig_size = (8,8)
        self.yaxis_format = FormatStrFormatter('%3.1f')

        #Preprocessing
        self.A = self.rotate_back(A)
        self.get_components()
        self.ts_cut = np.unravel_index(self.A.argmax(),self.A.shape)[0]

        #Configure logger
        self.logger = logging.getLogger(type(self).__name__)

        #Check data type
        if self.toption == 'simulation':
            try:
                self.target = kwargs['target']
            except:
                raise ValueError("Please specify list of target sources when plotting simulation results.")
        if self.input_type == 'timeseries':
            try:
                self.ny = kwargs['Tmat'].shape[0]
            except:
                raise ValueError("Please specify matrix representation of time series when using 1D representation.")

    def rotate_back(self,mat):
        """Rotate back and reshape"""
        #rotate matrix back
        if self.angle != 0.0:
            mat_rot = rotate(mat,-self.angle)
        else:
            mat_rot = mat
        #get indices of rotated matrix
        ny_r,nx_r = mat_rot.shape
        #calculate differences
        delta_y = int(np.round((ny_r - self.ny)/2.0))
        delta_x = int(np.round((nx_r - self.nx)/2.0))
        #Return cut and rotated matrix
        return mat_rot[delta_y:((ny_r - delta_y)),delta_x:((nx_r - delta_x))]

    def get_components(self):
        """Separate A matrix into components"""
        self.components = []
        for i in range(self.q):
            self.components.append(self.rotate_back(np.outer(self.u[:,i],self.v[i,:])))

    def plot_obs_pred_total(self,**kwargs):
        """Plot original observation and recovered result"""
        if self.input_type == 'matrix':
            fig,ax = plt.subplots(1,2,figsize=self.fig_size)
            plt.tight_layout()
            imT = ax[0].imshow(np.ma.masked_where(self.T<self.zero_tol*np.max(self.T),self.T),cmap=self.cm)
            imA = ax[1].imshow(np.ma.masked_where(self.A<self.zero_tol*np.max(self.A),self.A),cmap=self.cm)

            cbar1 = fig.colorbar(imT,cax=make_axes_locatable(ax[0]).append_axes("right","5%",pad="3%"),ticks=[np.min(self.T),(np.max(self.T)-np.min(self.T))/2.0,np.max(self.T)],format=self.yaxis_format)
            cbar2 = fig.colorbar(imA,cax=make_axes_locatable(ax[1]).append_axes("right","5%",pad="3%"),ticks=[np.min(self.A),(np.max(self.A)-np.min(self.A))/2.0,np.max(self.A)],format=self.yaxis_format)
            if 'peak_id' in kwargs and kwargs['peak_id']:
                self.logger.info("Finding peaks from separated images")
                self.source_id()
                ax[0].scatter(x=self.peak_id[:,1],y=self.peak_id[:,0],c='white',marker='x',s=15)

            ax[0].set_title(r'$T$, Observation',fontsize=self.fs)
            ax[1].set_title(r'$A$, Prediction',fontsize=self.fs)
            ax[0].set_xlim([0,np.shape(self.T)[1]])
            ax[0].set_ylim([0,np.shape(self.T)[0]])
            ax[1].set_xlim([0,np.shape(self.A)[1]])
            ax[1].set_ylim([0,np.shape(self.A)[0]])
            ax[0].set_yticks([])
            ax[0].set_xticks([])
            ax[1].set_yticks([])
            ax[1].set_xticks([])

        elif self.input_type == 'timeseries':
            fig = plt.figure(figsize=self.fig_size)
            ax = fig.gca()
            plt.tight_layout()
            ax.plot(self.T,'.k',label='Observation')
            ax.plot(self.A[self.ts_cut,:],'r',label='Prediction')
            ax.set_xlabel(r'$t$ (au)',fontsize=self.fs)
            ax.set_ylabel(r'$I$ (au)',fontsize=self.fs)
            ax.set_ylim([0,1])
            ax.set_xlim([0,len(self.T)])
            ax.set_yticks([np.min(self.T),(np.max(self.T)-np.min(self.T))/2.0,np.max(self.T)])
            ax.yaxis.set_major_formatter(self.yaxis_format)
            ax.legend(loc='best')

        else:
            raise ValueError("Invalid input type option.")

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_obs_pred_sources(self,**kwargs):
        """Plot recovered sources against the original components"""
        try:
            rows = max(self.q,len(self.target))
            pairs = self.match_closest()
        except:
            rows = self.q
            pairs = []
            [pairs.append((i,i)) for i in range(self.q)]

        if self.input_type == 'matrix':
            fig,ax = plt.subplots(2,rows,figsize=self.fig_size,sharex=True,sharey=True)
            plt.tight_layout()
            ax[0,0].set_ylabel(r'Sources',fontsize=self.fs)
            ax[1,0].set_ylabel(r'Predictions',fontsize=self.fs)
            for i in range(rows):
                try:
                    tmp_mask = np.ma.masked_where(self.target[pairs[i][0]]<self.zero_tol*np.max(self.target[pairs[i][0]]),self.target[pairs[i][0]])
                    im = ax[0,i].imshow(tmp_mask,cmap=self.cm)
                    ax[0,i].set_xlim([0,np.shape(self.target[pairs[i][0]])[1]])
                    ax[0,i].set_ylim([0,np.shape(self.target[pairs[i][0]])[0]])
                    ax[0,i].set_xticks([0,int(np.shape(self.target[pairs[i][0]])[1]/2),np.shape(self.target[pairs[i][0]])[1]])
                    ax[0,i].set_yticks([0,int(np.shape(self.target[pairs[i][0]])[0]/2),np.shape(self.target[pairs[i][0]])[0]])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

                try:
                    tmp_mask = np.ma.masked_where(self.target[pairs[i][1]]<self.zero_tol*np.max(self.target[pairs[i][1]]),self.target[pairs[i][1]])
                    im = ax[1,i].imshow(tmp_mask,cmap=self.cm)
                    ax[1,i].set_xlim([0,np.shape(self.target[pairs[i][1]])[1]])
                    ax[1,i].set_ylim([0,np.shape(self.target[pairs[i][1]])[0]])
                    ax[1,i].set_xticks([0,int(np.shape(self.target[pairs[i][1]])[1]/2),np.shape(self.target[pairs[i][1]])[1]])
                    ax[1,i].set_yticks([0,int(np.shape(self.target[pairs[i][1]])[0]/2),np.shape(self.target[pairs[i][1]])[0]])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

        elif self.input_type == 'timeseries':
            fig,ax = plt.subplots(rows,1,figsize=self.fig_size,sharex=True,sharey=True)
            plt.tight_layout()
            for i in range(rows):
                try:
                    ax[i].plot(self.target[pairs[i][0]],'.k',label='source')
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)
                try:
                    ax[i].plot(self.components[pairs[i][1]][self.ts_cut,:],'r',label='prediction')
                    ax[i].set_yticks([0.0,(np.max(self.components[pairs[i][1]][self.ts_cut,:]) - np.min(self.components[pairs[i][1]][self.ts_cut,:]))/2.0,np.max(self.components[pairs[i][1]][self.ts_cut,:])])
                    ax[i].yaxis.set_major_formatter(self.yaxis_format)
                    ax[i].set_ylim([0,1])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

            fig.text(0.07, 0.5, r'$I$ $\mathrm{(au)}$', ha='center',
                     va='center', rotation='vertical',fontsize=self.fs)
            ax[-1].set_xlabel(r'$t$ $\mathrm{(au)}$',fontsize=self.fs)
            ax[0].legend(loc='best')

        else:
            raise ValueError("Invalid input type option")

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_obs_pred_total_sources_ts(self,**kwargs):
        """Plot sources + total for observation and prediction"""
        fig = plt.figure(figsize=self.fig_size)
        ax = fig.gca()
        ax.plot(self.T,'.k',label='Observation')
        ax.plot(self.A[self.ts_cut,:],'r',label='Prediction')
        for i in range(self.q):
            ax.plot(self.components[i][self.ts_cut,:],'--b')
        ax.set_xlabel(r'$t$ (au)',fontsize=self.fs)
        ax.set_ylabel(r'$I$ (au)',fontsize=self.fs)
        ax.yaxis.set_major_formatter(self.yaxis_format)
        ax.set_ylim([0,1])
        ax.set_xlim([0,len(self.T)])
        ax.legend(loc='best')

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_div(self,**kwargs):
        """Plot divergence metric as function of iteration"""
        try:
            fig = plt.figure(figsize=self.fig_size)
            ax = fig.gca()
            ax.plot(self.div)
            ax.set_yscale('log')
            ax.set_xlim([0,len(self.div)])
            ax.set_ylim([np.min(self.div),np.max(self.div)])
            ax.set_title(r'Divergence Measure',fontsize=self.fs)
            ax.set_xlabel(r'iteration',fontsize=self.fs)
            ax.set_ylabel(r'$d(T,A)$',fontsize=self.fs)
        except AttributeError:
            self.logger.exception("Cannot plot divergence metric. self.div not set.")
            return

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def match_closest(self):
        """Create list of pairs of components and targets so that the plots correspond"""
        sources = range(self.q)
        pairs = []
        i_target = 0
        while sources != []:
            min_diff = 1.0e+50
            pairs.append(([],[]))

            for i in sources:
                if self.input_type == 'matrix':
                    diff = np.mean(np.fabs(self.target[i_target] - self.components[i]))
                else:
                    diff = np.mean(np.fabs(self.target[i_target] - self.components[i][self.ts_cut,:]))

                if diff < min_diff:
                    pairs.pop()
                    pairs.append((i_target,i))
                    min_diff = diff

            sources.remove(pairs[i_target][1])
            i_target += 1

        return pairs

    def source_id(self):
        """Find sources in image by picking out peak coordinates in target image"""
        tmp = []
        for c in self.components:
            centers = np.unravel_index(np.argmax(c),np.shape(c))
            self.logger.debug("(%d,%d)"%centers)
            tmp.append(centers)

        self.peak_id = np.array(tmp)
<commit_msg>fixing comparison plot<commit_after>#solarnmf_plotting.py

#Will Barnes
#3 April 2015

import logging
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
from scipy.ndimage.interpolation import rotate

class MakeBSSPlots(object):

    def __init__(self,toption,input_type,u,v,A,T,div=None,q=None,angle=0.0,fig_size=(8,8),print_format='eps',print_dpi=1000,**kwargs):
        self.toption = toption
        self.input_type = input_type
        self.u = u
        self.v = v
        self.A = A
        self.T = T

        if div:
            self.div
        if not q:
            self.q = self.u.shape[1]
        else:
            self.q = q

        self.ny,self.nx = np.shape(T)

        #set optional member variables
        self.angle = angle
        self.print_format = print_format
        self.print_dpi = print_dpi
        self.fs = 18
        self.cm = 'Blues'
        self.zero_tol = 1.e-5
        self.fig_size = (8,8)
        self.yaxis_format = FormatStrFormatter('%3.1f')

        #Preprocessing
        self.A = self.rotate_back(A)
        self.get_components()
        self.ts_cut = np.unravel_index(self.A.argmax(),self.A.shape)[0]

        #Configure logger
        self.logger = logging.getLogger(type(self).__name__)

        #Check data type
        if self.toption == 'simulation':
            try:
                self.target = kwargs['target']
            except:
                raise ValueError("Please specify list of target sources when plotting simulation results.")
        if self.input_type == 'timeseries':
            try:
                self.ny = kwargs['Tmat'].shape[0]
            except:
                raise ValueError("Please specify matrix representation of time series when using 1D representation.")

    def rotate_back(self,mat):
        """Rotate back and reshape"""
        #rotate matrix back
        if self.angle != 0.0:
            mat_rot = rotate(mat,-self.angle)
        else:
            mat_rot = mat
        #get indices of rotated matrix
        ny_r,nx_r = mat_rot.shape
        #calculate differences
        delta_y = int(np.round((ny_r - self.ny)/2.0))
        delta_x = int(np.round((nx_r - self.nx)/2.0))
        #Return cut and rotated matrix
        return mat_rot[delta_y:((ny_r - delta_y)),delta_x:((nx_r - delta_x))]

    def get_components(self):
        """Separate A matrix into components"""
        self.components = []
        for i in range(self.q):
            self.components.append(self.rotate_back(np.outer(self.u[:,i],self.v[i,:])))

    def plot_obs_pred_total(self,**kwargs):
        """Plot original observation and recovered result"""
        if self.input_type == 'matrix':
            fig,ax = plt.subplots(1,2,figsize=self.fig_size)
            plt.tight_layout()
            imT = ax[0].imshow(np.ma.masked_where(self.T<self.zero_tol*np.max(self.T),self.T),cmap=self.cm)
            imA = ax[1].imshow(np.ma.masked_where(self.A<self.zero_tol*np.max(self.A),self.A),cmap=self.cm)

            cbar1 = fig.colorbar(imT,cax=make_axes_locatable(ax[0]).append_axes("right","5%",pad="3%"),ticks=[np.min(self.T),(np.max(self.T)-np.min(self.T))/2.0,np.max(self.T)],format=self.yaxis_format)
            cbar2 = fig.colorbar(imA,cax=make_axes_locatable(ax[1]).append_axes("right","5%",pad="3%"),ticks=[np.min(self.A),(np.max(self.A)-np.min(self.A))/2.0,np.max(self.A)],format=self.yaxis_format)
            if 'peak_id' in kwargs and kwargs['peak_id']:
                self.logger.info("Finding peaks from separated images")
                self.source_id()
                ax[0].scatter(x=self.peak_id[:,1],y=self.peak_id[:,0],c='white',marker='x',s=15)

            ax[0].set_title(r'$T$, Observation',fontsize=self.fs)
            ax[1].set_title(r'$A$, Prediction',fontsize=self.fs)
            ax[0].set_xlim([0,np.shape(self.T)[1]])
            ax[0].set_ylim([0,np.shape(self.T)[0]])
            ax[1].set_xlim([0,np.shape(self.A)[1]])
            ax[1].set_ylim([0,np.shape(self.A)[0]])
            ax[0].set_yticks([])
            ax[0].set_xticks([])
            ax[1].set_yticks([])
            ax[1].set_xticks([])

        elif self.input_type == 'timeseries':
            fig = plt.figure(figsize=self.fig_size)
            ax = fig.gca()
            plt.tight_layout()
            ax.plot(self.T,'.k',label='Observation')
            ax.plot(self.A[self.ts_cut,:],'r',label='Prediction')
            ax.set_xlabel(r'$t$ (au)',fontsize=self.fs)
            ax.set_ylabel(r'$I$ (au)',fontsize=self.fs)
            ax.set_ylim([0,1])
            ax.set_xlim([0,len(self.T)])
            ax.set_yticks([np.min(self.T),(np.max(self.T)-np.min(self.T))/2.0,np.max(self.T)])
            ax.yaxis.set_major_formatter(self.yaxis_format)
            ax.legend(loc='best')

        else:
            raise ValueError("Invalid input type option.")

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_obs_pred_sources(self,**kwargs):
        """Plot recovered sources against the original components"""
        try:
            rows = max(self.q,len(self.target))
            pairs = self.match_closest()
        except:
            rows = self.q
            pairs = []
            [pairs.append((i,i)) for i in range(self.q)]

        if self.input_type == 'matrix':
            fig,ax = plt.subplots(2,rows,figsize=self.fig_size,sharex=True,sharey=True)
            plt.tight_layout()
            ax[0,0].set_ylabel(r'Sources',fontsize=self.fs)
            ax[1,0].set_ylabel(r'Predictions',fontsize=self.fs)
            for i in range(rows):
                try:
                    tmp_mask = np.ma.masked_where(self.target[pairs[i][0]]<self.zero_tol*np.max(self.target[pairs[i][0]]),self.target[pairs[i][0]])
                    im = ax[0,i].imshow(tmp_mask,cmap=self.cm)
                    ax[0,i].set_xlim([0,np.shape(self.target[pairs[i][0]])[1]])
                    ax[0,i].set_ylim([0,np.shape(self.target[pairs[i][0]])[0]])
                    ax[0,i].set_xticks([0,int(np.shape(self.target[pairs[i][0]])[1]/2),np.shape(self.target[pairs[i][0]])[1]])
                    ax[0,i].set_yticks([0,int(np.shape(self.target[pairs[i][0]])[0]/2),np.shape(self.target[pairs[i][0]])[0]])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

                try:
                    tmp_mask = np.ma.masked_where(self.components[pairs[i][1]]<self.zero_tol*np.max(self.components[pairs[i][1]]),self.components[pairs[i][1]])
                    im = ax[1,i].imshow(tmp_mask,cmap=self.cm)
                    ax[1,i].set_xlim([0,np.shape(self.target[pairs[i][1]])[1]])
                    ax[1,i].set_ylim([0,np.shape(self.target[pairs[i][1]])[0]])
                    ax[1,i].set_xticks([0,int(np.shape(self.target[pairs[i][1]])[1]/2),np.shape(self.target[pairs[i][1]])[1]])
                    ax[1,i].set_yticks([0,int(np.shape(self.target[pairs[i][1]])[0]/2),np.shape(self.target[pairs[i][1]])[0]])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

        elif self.input_type == 'timeseries':
            fig,ax = plt.subplots(rows,1,figsize=self.fig_size,sharex=True,sharey=True)
            plt.tight_layout()
            for i in range(rows):
                try:
                    ax[i].plot(self.target[pairs[i][0]],'.k',label='source')
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)
                try:
                    ax[i].plot(self.components[pairs[i][1]][self.ts_cut,:],'r',label='prediction')
                    ax[i].set_yticks([0.0,(np.max(self.components[pairs[i][1]][self.ts_cut,:]) - np.min(self.components[pairs[i][1]][self.ts_cut,:]))/2.0,np.max(self.components[pairs[i][1]][self.ts_cut,:])])
                    ax[i].yaxis.set_major_formatter(self.yaxis_format)
                    ax[i].set_ylim([0,1])
                except IndexError:
                    self.logger.debug("Skipping source entry %d, out of range."%i)

            fig.text(0.07, 0.5, r'$I$ $\mathrm{(au)}$', ha='center',
                     va='center', rotation='vertical',fontsize=self.fs)
            ax[-1].set_xlabel(r'$t$ $\mathrm{(au)}$',fontsize=self.fs)
            ax[0].legend(loc='best')

        else:
            raise ValueError("Invalid input type option")

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_obs_pred_total_sources_ts(self,**kwargs):
        """Plot sources + total for observation and prediction"""
        fig = plt.figure(figsize=self.fig_size)
        ax = fig.gca()
        ax.plot(self.T,'.k',label='Observation')
        ax.plot(self.A[self.ts_cut,:],'r',label='Prediction')
        for i in range(self.q):
            ax.plot(self.components[i][self.ts_cut,:],'--b')
        ax.set_xlabel(r'$t$ (au)',fontsize=self.fs)
        ax.set_ylabel(r'$I$ (au)',fontsize=self.fs)
        ax.yaxis.set_major_formatter(self.yaxis_format)
        ax.set_ylim([0,1])
        ax.set_xlim([0,len(self.T)])
        ax.legend(loc='best')

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def plot_div(self,**kwargs):
        """Plot divergence metric as function of iteration"""
        try:
            fig = plt.figure(figsize=self.fig_size)
            ax = fig.gca()
            ax.plot(self.div)
            ax.set_yscale('log')
            ax.set_xlim([0,len(self.div)])
            ax.set_ylim([np.min(self.div),np.max(self.div)])
            ax.set_title(r'Divergence Measure',fontsize=self.fs)
            ax.set_xlabel(r'iteration',fontsize=self.fs)
            ax.set_ylabel(r'$d(T,A)$',fontsize=self.fs)
        except AttributeError:
            self.logger.exception("Cannot plot divergence metric. self.div not set.")
            return

        if 'print_fig_filename' in kwargs:
            plt.savefig(kwargs['print_fig_filename']+'.'+self.print_format,format=self.print_format,dpi=self.print_dpi)
            plt.close('all')
        else:
            plt.show()

    def match_closest(self):
        """Create list of pairs of components and targets so that the plots correspond"""
        sources = range(self.q)
        pairs = []
        i_target = 0
        while sources != []:
            min_diff = 1.0e+50
            pairs.append(([],[]))

            for i in sources:
                if self.input_type == 'matrix':
                    diff = np.mean(np.fabs(self.target[i_target] - self.components[i]))
                else:
                    diff = np.mean(np.fabs(self.target[i_target] - self.components[i][self.ts_cut,:]))

                if diff < min_diff:
                    pairs.pop()
                    pairs.append((i_target,i))
                    min_diff = diff

            sources.remove(pairs[i_target][1])
            i_target += 1

        return pairs

    def source_id(self):
        """Find sources in image by picking out peak coordinates in target image"""
        tmp = []
        for c in self.components:
            centers = np.unravel_index(np.argmax(c),np.shape(c))
            self.logger.debug("(%d,%d)"%centers)
            tmp.append(centers)

        self.peak_id = np.array(tmp)
<|endoftext|>


<commit_before>from copy import copy
from cycgkit.cgtypes import *

from ..Base3DObjectClass import Base3DObject
from ..Logging import logLevelsEnum, _Logger

from ..physics_management.physicsModule import bodyShapesEnum

class SimpleCamera(Base3DObject):
    def get_zFar(self):
        return self._p_zFar

    def set_zFar(self, value):
        self._p_zFar = value

    zFar = property(fget=get_zFar, fset=set_zFar)

    def get_zNear(self):
        return self._p_zNear

    def set_zNear(self, value):
        self._p_zNear = value

    zNear = property(fget=get_zNear, fset=set_zNear)

    def get_FOV(self):
        return self._p_fov

    def set_FOV(self, value):
        self._p_fov = value
        self.updateFOV(self.width, self.height)

    FOV = property(fget=get_FOV, fset=set_FOV)

    # def get_lookAt(self):
    # return self.lookAt
    #
    # lookAt = property(fget=get_lookAt)

    def __init__(self, position=vec3(0), rotation=vec3(0), size=None, shape=bodyShapesEnum.capsule, fov=45, zFar=5000, zNear=1, ID=''):
        """
        Basic Camera
        @type position: list
        @type size: list
        @type rotation: list
        @type sceneWidth: int
        @type sceneHeight: int
        @rtype : SimpleCamera
        """
        if not size:
            size = [1, 5, 1]

        super(SimpleCamera, self).__init__(position, rotation, 1, size, shape, 1.0, True, ID)
        self.__sw = 0
        self.__sh = 0
        self._vm = None
        self.__lastpos = None
        self.__lastrot = None
        self._p_fov = fov
        self._p_zFar = zFar
        self._p_zNear = zNear
        self.lookAtConstant = vec3(0, 0, 1)
        self.upConstant = vec3(0, 1, 0)
        self.lookAt = self.lookAtConstant
        self.lookAtFixed = self.lookAtConstant
        self.projectionMatrix = None

        self._update(True)

    def updateFOV(self, width, height):
        try:
            self.projectionMatrix = mat4.perspective(self._p_fov, float(width) / float(height),
                                                     self._p_zNear, self._p_zFar)
        except Exception as ex:
            _Logger.log(str(ex), logLevelsEnum.warning)
            self.projectionMatrix = mat4.perspective(45, 640.0 / 480.0, 0.1, 500)

    def _update(self, force=False):
        """
         Updates the transformation (view matrix) of the camera, and returns it.
        @rtype : mat4
        """
        if super(SimpleCamera, self)._update() or force:
            self.lookAtFixed = self._rotationMatrix * self.lookAtConstant
            self.lookAt = self._position + self.lookAtFixed
            self._vm = self.lookAtRH(self._position, self.lookAt, self._rotationMatrix * self.upConstant)
            self.__lastpos = copy(self._position)
            self.__lastrot = copy(self._rotation)

        return self._vm

    @staticmethod
    def lookAtRH(eye, target, up):
        # http://devmaster.net/posts/7255/glulookat-alternative-and-building-look-at-matrix
        # http://webglfactory.blogspot.mx/2011/06/how-to-create-view-matrix.html

        try:
            vz = vec3(target - eye).normalize()
            vx = vec3(vec3(vz).cross(up)).normalize()
            vy = vec3(vx).cross(vz)
            v4e = vec4(eye)
            v4e.w = 1

            ViewMatrix = mat4(
                vec4(vx),
                vec4(vy),
                vec4(-vz),
                # vec4(0, 0, 0, 1))
                v4e)
            return ViewMatrix.inverse()  # * mat4.translation(eye)

        except Exception as ex:
            raise Exception('lookAtRH error: ' + ex.message)
<commit_msg>Better ID.<commit_after>from copy import copy
from cycgkit.cgtypes import *

from ..Base3DObjectClass import Base3DObject
from ..Logging import logLevelsEnum, _Logger

from ..physics_management.physicsModule import bodyShapesEnum

class SimpleCamera(Base3DObject):
    def get_zFar(self):
        return self._p_zFar

    def set_zFar(self, value):
        self._p_zFar = value

    zFar = property(fget=get_zFar, fset=set_zFar)

    def get_zNear(self):
        return self._p_zNear

    def set_zNear(self, value):
        self._p_zNear = value

    zNear = property(fget=get_zNear, fset=set_zNear)

    def get_FOV(self):
        return self._p_fov

    def set_FOV(self, value):
        self._p_fov = value
        self.updateFOV(self.width, self.height)

    FOV = property(fget=get_FOV, fset=set_FOV)

    # def get_lookAt(self):
    # return self.lookAt
    #
    # lookAt = property(fget=get_lookAt)

    def __init__(self, position=vec3(0), rotation=vec3(0), size=None, shape=bodyShapesEnum.capsule,
                 fov=45, zFar=5000, zNear=1, ID=None):
        """
        Basic Camera
        @type position: list
        @type size: list
        @type rotation: list
        @type sceneWidth: int
        @type sceneHeight: int
        @rtype : SimpleCamera
        """
        if ID is None:
            ID = id(self)
        if not size:
            size = [1, 5, 1]

        super(SimpleCamera, self).__init__(position, rotation, 1, size, shape, 1.0, True, ID)
        self.__sw = 0
        self.__sh = 0
        self._vm = None
        self.__lastpos = None
        self.__lastrot = None
        self._p_fov = fov
        self._p_zFar = zFar
        self._p_zNear = zNear
        self.lookAtConstant = vec3(0, 0, 1)
        self.upConstant = vec3(0, 1, 0)
        self.lookAt = self.lookAtConstant
        self.lookAtFixed = self.lookAtConstant
        self.projectionMatrix = None

        self._update(True)

    def updateFOV(self, width, height):
        try:
            self.projectionMatrix = mat4.perspective(self._p_fov, float(width) / float(height),
                                                     self._p_zNear, self._p_zFar)
        except Exception as ex:
            _Logger.log(str(ex), logLevelsEnum.warning)
            self.projectionMatrix = mat4.perspective(45, 640.0 / 480.0, 0.1, 500)

    def _update(self, force=False):
        """
         Updates the transformation (view matrix) of the camera, and returns it.
        @rtype : mat4
        """
        if super(SimpleCamera, self)._update() or force:
            self.lookAtFixed = self._rotationMatrix * self.lookAtConstant
            self.lookAt = self._position + self.lookAtFixed
            self._vm = self.lookAtRH(self._position, self.lookAt, self._rotationMatrix * self.upConstant)
            self.__lastpos = copy(self._position)
            self.__lastrot = copy(self._rotation)

        return self._vm

    @staticmethod
    def lookAtRH(eye, target, up):
        # http://devmaster.net/posts/7255/glulookat-alternative-and-building-look-at-matrix
        # http://webglfactory.blogspot.mx/2011/06/how-to-create-view-matrix.html

        try:
            vz = vec3(target - eye).normalize()
            vx = vec3(vec3(vz).cross(up)).normalize()
            vy = vec3(vx).cross(vz)
            v4e = vec4(eye)
            v4e.w = 1

            ViewMatrix = mat4(
                vec4(vx),
                vec4(vy),
                vec4(-vz),
                # vec4(0, 0, 0, 1))
                v4e)
            return ViewMatrix.inverse()  # * mat4.translation(eye)

        except Exception as ex:
            raise Exception('lookAtRH error: ' + ex.message)
<|endoftext|>