<commit_before>import numpy as np
import scipy.sparse as sp
from . import regions as reg

class Field:
    """Base class for all fields."""

    def material_vector(self, mat_parameter):
        """Get a vector that contains the specified material parameter for every point of the
        field."""

        mat_vector = np.zeros(self.num_points)

        for mat_reg in self.material_regions:
            for mat in mat_reg.materials:
                if hasattr(mat, mat_parameter):
                    mat_vector[mat_reg.region.indices] = getattr(mat, mat_parameter)

        return mat_vector

    def get_point_region(self, position, name=''):
        """Creates a point region at the given position."""

        return reg.PointRegion([self.get_index(position)], position, name=name)

class Field1D(Field):
    """Class for one dimensional fields."""

    def __init__(self, x_samples, x_delta, t_samples, t_delta, material):
        self.x = Dimension(x_samples, x_delta)
        self.t = Dimension(t_samples, t_delta)

        # add main material
        self.material_regions = [reg.MaterialRegion(reg.LineRegion(
            np.arange(self.x.samples, dtype='int_'), [0, max(self.x.vector)], 'main'), material)]

    @property
    def num_points(self):
        return self.x.samples

    def d_x(self, factors=None, variant='forward'):
        """Creates a sparse matrix for computing the first derivative with respect to x multiplied
        by factors given for every point. Uses forward difference quotient by default, specify
        backward=True if required otherwise"""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        variants = {
            'forward': sp.dia_matrix((np.array([-factors, factors]), [0, 1]),
                                     shape=(self.num_points, self.num_points)),
            'central': sp.dia_matrix((np.array([-factors/2, factors/2]), [-1, 1]),
                                     shape=(self.num_points, self.num_points)),
            'backward': sp.dia_matrix((np.array([-factors, factors]), [-1, 0]),
                                      shape=(self.num_points, self.num_points))
        }

        return variants.get(variant)

    def d_x2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]), [-1, 0, 1]),
                             shape=(self.num_points, self.num_points))

    def get_index(self, position):
        """Returns the index of the point a the given position."""

        return self.x.get_index(position)

    def get_position(self, index):
        """Returns the position of a point with the given index."""

        return self.x.vector[index]

    def get_line_region(self, position, name=''):
        """Creates a line region at the given position (start, end), inclusive."""

        return reg.LineRegion([index for index in range(self.get_index(position[0]),
                                                        self.get_index(position[1]) + 1)],
                              position, name=name)

class Field2D(Field):
    """Class for two dimensional fields."""

    def __init__(self, x_samples, x_delta, y_samples, y_delta, t_samples, t_delta, material):
        self.x = Dimension(x_samples, x_delta)
        self.y = Dimension(y_samples, y_delta)
        self.t = Dimension(t_samples, t_delta)

        # add main material
        self.material_regions = [reg.MaterialRegion(reg.RectRegion(
            np.arange(self.num_points, dtype='int_'),
            [0, max(self.x.vector), 0, max(self.y.vector)], 'main'), material)]

    @property
    def num_points(self):
        return self.x.samples * self.y.samples

    def d_x(self, factors=None, backward=False):
        """Creates a sparse matrix for computing the first derivative with respect to x multiplied
        by factors given for every point. Uses forward difference quotient by default, specify
        backward=True if required otherwise"""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        if not backward:
            return sp.dia_matrix((np.array([-factors, factors]), [0, 1]),
                                 shape=(self.num_points, self.num_points))
        else:
            return sp.dia_matrix((np.array([-factors, factors]), [-1, 0]),
                                 shape=(self.num_points, self.num_points))

    def d_y(self, factors=None, backward=False):
        """Creates a sparse matrix for computing the first derivative with respect to y multiplied
        by factors given for every point. Uses forward difference quotient by default, specify
        backward=True if required otherwise"""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        if not backward:
            return sp.dia_matrix((np.array([-factors, factors]), [0, self.x.samples]),
                                 shape=(self.num_points, self.num_points))
        else:
            return sp.dia_matrix((np.array([-factors, factors]), [-self.x.samples, 0]),
                                 shape=(self.num_points, self.num_points))

    def d_x2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]), [-1, 0, 1]),
                             shape=(self.num_points, self.num_points))

    def d_y2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]),
                              [-self.x.samples, 0, self.x.samples]),
                             shape=(self.num_points, self.num_points))

    def get_index(self, position):
        """Returns the index of the point a the given position."""

        return self.x.get_index(position[0]) + self.y.get_index(position[1]) * self.x.samples

    def get_position(self, index):
        """Returns the position of a point with the given index."""

        return self.x.vector[index % self.x.samples], self.y.vector[int(index / self.x.samples)]

    def get_line_region(self, position, name=''):
        """Creates a line region at the given position (start_x, start_y, end_x, end_y),
        inclusive."""

        start_idx = self.get_index(position[:2])
        end_idx = self.get_index(position[2:])

        x_diff = start_idx % self.x.samples - end_idx % self.x.samples
        y_diff = int(start_idx / self.x.samples) - int(end_idx / self.x.samples)

        num_points = max(np.abs([x_diff, y_diff]))
        point_indices = []

        for ii in range(num_points + 1):

            x_position = start_idx % self.x.samples - np.round(ii / num_points * x_diff)
            y_position = int(start_idx / self.x.samples) - np.round(ii / num_points * y_diff)
            point_indices.append(int(x_position + self.x.samples * y_position))

        return reg.LineRegion(point_indices, position, name=name)

    def get_rect_region(self, position, name=''):
        """Creates a rectangular region at the given position (origin_x, origin_y, size_x, size_y),
        inclusive, origin is the lower left corner."""

        x_start = self.x.get_index(position[0])
        y_start = self.y.get_index(position[1])
        x_end = self.x.get_index(position[0] + position[2])
        y_end = self.y.get_index(position[1] + position[3])

        x_start, x_end = min(x_start, x_end), max(x_start, x_end)
        y_start, y_end = min(y_start, y_end), max(y_start, y_end)

        return reg.RectRegion([x + y * self.x.samples for x in range(x_start, x_end + 1)
                               for y in range(y_start, y_end + 1)], position, name)

class Dimension:
    """Represents a space or time axis."""

    def __init__(self, samples, increment):

        self.samples = int(samples)
        self.increment = increment
        self.snap_radius = np.finfo(float).eps * 10

    @property
    def vector(self):
        return np.arange(start=0, stop=self.samples) * self.increment

    def get_index(self, value):
        """Returns the index of a given value."""

        index, = np.where(np.abs(self.vector - value) <= self.snap_radius)
        assert len(index) < 2, "Multiple points found within snap radius of given value."
        assert len(index) > 0, "No point found within snap radius of given value."

        return int(index)

class FieldComponent:
    """A single component of a field (e.g. electric field in the x direction)."""

    def __init__(self, num_points):

        # values of the field component
        self.values = np.zeros(num_points)
        # list with objects of type Boundary
        self.boundaries = []
        # list with objects of type Output
        self.outputs = []

    def apply_bounds(self, step):
        """Applies the  boundary conditions to the field component."""

        for bound in self.boundaries:
            if np.ndim(bound.value) == 0 or \
                    (np.ndim(bound.value) == 1 and type(bound.value) == list):
                # if a single value or a list of single values for each index is given
                self.values[bound.region.indices] = \
                    (bound.additive * self.values[bound.region.indices] + bound.value)
            elif type(bound.value) == np.ndarray:
                # if a signals is given
                self.values[bound.region.indices] = \
                    (bound.additive * self.values[bound.region.indices] + bound.value[step])
            else:
                # if a list of signals for each index is given
                for signal, ii in enumerate(bound.value):
                    self.values[bound.region.indices[ii]] = \
                        (bound.additive * self.values[bound.region.indices[ii]] + signal[step])

    def write_outputs(self):
        """Writes the values of the field component to the outputs."""

        for output in self.outputs:

            if not output.signals:
                output.signals = [[self.values[index]] for index in output.region.indices]
            else:
                [signal.append(self.values[index]) for index, signal in
                 zip(output.region.indices, output.signals)]

<commit_msg>Update comments to reflect changes made in last commit.<commit_after>import numpy as np
import scipy.sparse as sp
from . import regions as reg

class Field:
    """Base class for all fields."""

    def material_vector(self, mat_parameter):
        """Get a vector that contains the specified material parameter for every point of the
        field."""

        mat_vector = np.zeros(self.num_points)

        for mat_reg in self.material_regions:
            for mat in mat_reg.materials:
                if hasattr(mat, mat_parameter):
                    mat_vector[mat_reg.region.indices] = getattr(mat, mat_parameter)

        return mat_vector

    def get_point_region(self, position, name=''):
        """Creates a point region at the given position."""

        return reg.PointRegion([self.get_index(position)], position, name=name)

class Field1D(Field):
    """Class for one dimensional fields."""

    def __init__(self, x_samples, x_delta, t_samples, t_delta, material):
        self.x = Dimension(x_samples, x_delta)
        self.t = Dimension(t_samples, t_delta)

        # add main material
        self.material_regions = [reg.MaterialRegion(reg.LineRegion(
            np.arange(self.x.samples, dtype='int_'), [0, max(self.x.vector)], 'main'), material)]

    @property
    def num_points(self):
        return self.x.samples

    def d_x(self, factors=None, variant='forward'):
        """Creates a sparse matrix for computing the first derivative with respect to x multiplied
        by factors given for every point. Uses forward difference quotient by default."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        variants = {
            'forward': sp.dia_matrix((np.array([-factors, factors]), [0, 1]),
                                     shape=(self.num_points, self.num_points)),
            'central': sp.dia_matrix((np.array([-factors/2, factors/2]), [-1, 1]),
                                     shape=(self.num_points, self.num_points)),
            'backward': sp.dia_matrix((np.array([-factors, factors]), [-1, 0]),
                                      shape=(self.num_points, self.num_points))
        }

        return variants.get(variant)

    def d_x2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]), [-1, 0, 1]),
                             shape=(self.num_points, self.num_points))

    def get_index(self, position):
        """Returns the index of the point a the given position."""

        return self.x.get_index(position)

    def get_position(self, index):
        """Returns the position of a point with the given index."""

        return self.x.vector[index]

    def get_line_region(self, position, name=''):
        """Creates a line region at the given position (start, end), inclusive."""

        return reg.LineRegion([index for index in range(self.get_index(position[0]),
                                                        self.get_index(position[1]) + 1)],
                              position, name=name)

class Field2D(Field):
    """Class for two dimensional fields."""

    def __init__(self, x_samples, x_delta, y_samples, y_delta, t_samples, t_delta, material):
        self.x = Dimension(x_samples, x_delta)
        self.y = Dimension(y_samples, y_delta)
        self.t = Dimension(t_samples, t_delta)

        # add main material
        self.material_regions = [reg.MaterialRegion(reg.RectRegion(
            np.arange(self.num_points, dtype='int_'),
            [0, max(self.x.vector), 0, max(self.y.vector)], 'main'), material)]

    @property
    def num_points(self):
        return self.x.samples * self.y.samples

    def d_x(self, factors=None, backward=False):
        """Creates a sparse matrix for computing the first derivative with respect to x multiplied
        by factors given for every point. Uses forward difference quotient by default, specify
        backward=True if required otherwise"""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        if not backward:
            return sp.dia_matrix((np.array([-factors, factors]), [0, 1]),
                                 shape=(self.num_points, self.num_points))
        else:
            return sp.dia_matrix((np.array([-factors, factors]), [-1, 0]),
                                 shape=(self.num_points, self.num_points))

    def d_y(self, factors=None, backward=False):
        """Creates a sparse matrix for computing the first derivative with respect to y multiplied
        by factors given for every point. Uses forward difference quotient by default, specify
        backward=True if required otherwise"""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        if not backward:
            return sp.dia_matrix((np.array([-factors, factors]), [0, self.x.samples]),
                                 shape=(self.num_points, self.num_points))
        else:
            return sp.dia_matrix((np.array([-factors, factors]), [-self.x.samples, 0]),
                                 shape=(self.num_points, self.num_points))

    def d_x2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]), [-1, 0, 1]),
                             shape=(self.num_points, self.num_points))

    def d_y2(self, factors=None):
        """Creates a sparse matrix for computing the second derivative with respect to x multiplied
        by factors given for every point."""

        # use ones as factors if none are specified
        if factors is None:
            factors = np.array(1).repeat(self.num_points)

        return sp.dia_matrix((np.array([factors, -2*factors, factors]),
                              [-self.x.samples, 0, self.x.samples]),
                             shape=(self.num_points, self.num_points))

    def get_index(self, position):
        """Returns the index of the point a the given position."""

        return self.x.get_index(position[0]) + self.y.get_index(position[1]) * self.x.samples

    def get_position(self, index):
        """Returns the position of a point with the given index."""

        return self.x.vector[index % self.x.samples], self.y.vector[int(index / self.x.samples)]

    def get_line_region(self, position, name=''):
        """Creates a line region at the given position (start_x, start_y, end_x, end_y),
        inclusive."""

        start_idx = self.get_index(position[:2])
        end_idx = self.get_index(position[2:])

        x_diff = start_idx % self.x.samples - end_idx % self.x.samples
        y_diff = int(start_idx / self.x.samples) - int(end_idx / self.x.samples)

        num_points = max(np.abs([x_diff, y_diff]))
        point_indices = []

        for ii in range(num_points + 1):

            x_position = start_idx % self.x.samples - np.round(ii / num_points * x_diff)
            y_position = int(start_idx / self.x.samples) - np.round(ii / num_points * y_diff)
            point_indices.append(int(x_position + self.x.samples * y_position))

        return reg.LineRegion(point_indices, position, name=name)

    def get_rect_region(self, position, name=''):
        """Creates a rectangular region at the given position (origin_x, origin_y, size_x, size_y),
        inclusive, origin is the lower left corner."""

        x_start = self.x.get_index(position[0])
        y_start = self.y.get_index(position[1])
        x_end = self.x.get_index(position[0] + position[2])
        y_end = self.y.get_index(position[1] + position[3])

        x_start, x_end = min(x_start, x_end), max(x_start, x_end)
        y_start, y_end = min(y_start, y_end), max(y_start, y_end)

        return reg.RectRegion([x + y * self.x.samples for x in range(x_start, x_end + 1)
                               for y in range(y_start, y_end + 1)], position, name)

class Dimension:
    """Represents a space or time axis."""

    def __init__(self, samples, increment):

        self.samples = int(samples)
        self.increment = increment
        self.snap_radius = np.finfo(float).eps * 10

    @property
    def vector(self):
        return np.arange(start=0, stop=self.samples) * self.increment

    def get_index(self, value):
        """Returns the index of a given value."""

        index, = np.where(np.abs(self.vector - value) <= self.snap_radius)
        assert len(index) < 2, "Multiple points found within snap radius of given value."
        assert len(index) > 0, "No point found within snap radius of given value."

        return int(index)

class FieldComponent:
    """A single component of a field (e.g. electric field in the x direction)."""

    def __init__(self, num_points):

        # values of the field component
        self.values = np.zeros(num_points)
        # list with objects of type Boundary
        self.boundaries = []
        # list with objects of type Output
        self.outputs = []

    def apply_bounds(self, step):
        """Applies the  boundary conditions to the field component."""

        for bound in self.boundaries:
            if np.ndim(bound.value) == 0 or \
                    (np.ndim(bound.value) == 1 and type(bound.value) == list):
                # if a single value or a list of single values for each index is given
                self.values[bound.region.indices] = \
                    (bound.additive * self.values[bound.region.indices] + bound.value)
            elif type(bound.value) == np.ndarray:
                # if a signals is given
                self.values[bound.region.indices] = \
                    (bound.additive * self.values[bound.region.indices] + bound.value[step])
            else:
                # if a list of signals for each index is given
                for signal, ii in enumerate(bound.value):
                    self.values[bound.region.indices[ii]] = \
                        (bound.additive * self.values[bound.region.indices[ii]] + signal[step])

    def write_outputs(self):
        """Writes the values of the field component to the outputs."""

        for output in self.outputs:

            if not output.signals:
                output.signals = [[self.values[index]] for index in output.region.indices]
            else:
                [signal.append(self.values[index]) for index, signal in
                 zip(output.region.indices, output.signals)]

<|endoftext|>


<commit_before># ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2007 Alex Holkner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the pyglet nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

'''Event dispatch framework.

All objects that produce events in pyglet implement `EventDispatcher`,
providing a consistent interface for registering and manipulating event
handlers.  A commonly used event dispatcher is `pyglet.window.Window`.

Event types
===========

For each event dispatcher there is a set of events that it dispatches; these
correspond with the type of event handlers you can attach.  Event types are
identified by their name, for example, ''on_resize''.  If you are creating a
new class which implements `EventDispatcher`, you must call
`EventDispatcher.register_event_type` for each event type.

Attaching event handlers
========================

An event handler is simply a function or method.  You can attach an event
handler by setting the appropriate function on the instance::

    def on_resize(width, height):
        # ...
    dispatcher.on_resize = on_resize

There is also a convenience decorator that reduces typing::

    @dispatcher.event
    def on_resize(width, height):
        # ...

You may prefer to subclass and override the event handlers instead::

    class MyDispatcher(DispatcherClass):
        def on_resize(self, width, height):
            # ...

Event handler stack
===================

When attaching an event handler to a dispatcher using the above methods, it
replaces any existing handler (causing the original handler to no longer be
called).  Each dispatcher maintains a stack of event handlers, allowing you to
insert an event handler "above" the existing one rather than replacing it.

There are two main use cases for "pushing" event handlers:

* Temporarily intercepting the events coming from the dispatcher by pushing a
  custom set of handlers onto the dispatcher, then later "popping" them all
  off at once.
* Creating "chains" of event handlers, where the event propogates from the
  top-most (most recently added) handler to the bottom, until a handler
  takes care of it.

Use `EventDispatcher.push_handlers` to create a new level in the stack and
attach handlers to it.  You can push several handlers at once::

    dispatcher.push_handlers(on_resize, on_key_press)

If your function handlers have different names to the events they handle, use
keyword arguments::

    dispatcher.push_handlers(on_resize=my_resize,
                             on_key_press=my_key_press)

After an event handler has processed an event, it is passed on to the
next-lowest event handler, unless the handler returns `EVENT_HANDLED`, which
prevents further propogation.

To remove all handlers on the top stack level, use
`EventDispatcher.pop_handlers`.

Note that any handlers pushed onto the stack have precedence over the
handlers set directly on the instance (for example, using the methods
described in the previous section), regardless of when they were set.
For example, handler ``foo`` is called before handler ``bar`` in the following
example::

    dispatcher.push_handlers(on_resize=foo)
    dispatcher.on_resize = bar

Dispatching events
==================

pyglet uses a single-threaded model for all application code.  Event
handlers are only ever invoked as a result of calling
EventDispatcher.dispatch_events`.

It is up to the specific event dispatcher to queue relevant events until they
can be dispatched, at which point the handlers are called in the order the
events were originally generated.

This implies that your application runs with a main loop that continously
updates the application state and checks for new events::

    while True:
        dispatcher.dispatch_events()
        # ... additional per-frame processing

Not all event dispatchers require the call to ``dispatch_events``; check with
the particular class documentation.

'''

__docformat__ = 'restructuredtext'
__version__ = '$Id$'

import inspect

EVENT_HANDLED = True
EVENT_UNHANDLED = None

class EventException(Exception):
    '''An exception raised when an event handler could not be attached.
    '''
    pass

class EventDispatcher(object):
    '''Generic event dispatcher interface.

    See the module docstring for usage.
    '''
    # Placeholder empty stack; real stack is created only if needed
    _event_stack = ()

    @classmethod
    def register_event_type(cls, name):
        '''Register an event type with the dispatcher.

        Registering event types allows the dispatcher to validate event
        handler names as they are attached, and to search attached objects for
        suitable handlers.

        :Parameters:
            `name` : str
                Name of the event to register.

        '''
        if not hasattr(cls, 'event_types'):
            cls.event_types = []
        cls.event_types.append(name)
        return name

    def push_handlers(self, *args, **kwargs):
        '''Push a level onto the top of the handler stack, then attach zero or
        more event handlers.

        If keyword arguments are given, they name the event type to attach.
        Otherwise, a callable's `__name__` attribute will be used.  Any other
        object may also be specified, in which case it will be searched for
        callables with event names.
        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        # Place dict full of new handlers at beginning of stack
        self._event_stack.insert(0, {})
        self.set_handlers(*args, **kwargs)

    def set_handlers(self, *args, **kwargs):
        '''Attach one or more event handlers to the top level of the handler
        stack.

        See `push_handlers` for the accepted argument types.
        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        for object in args:
            if inspect.isroutine(object):
                # Single magically named function
                name = object.__name__
                if name not in self.event_types:
                    raise EventException('Unknown event "%s"' % name)
                self.set_handler(name, object)
            else:
                # Single instance with magically named methods
                for name, handler in inspect.getmembers(object):
                    if name in self.event_types:
                        self.set_handler(name, handler)
        for name, handler in kwargs.items():
            # Function for handling given event (no magic)
            if name not in self.event_types:
                raise EventException('Unknown event "%s"' % name)
            self.set_handler(name, handler)

    def set_handler(self, name, handler):
        '''Attach a single event handler.

        :Parameters:
            `name` : str
                Name of the event type to attach to.
            `handler` : callable
                Event handler to attach.

        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        self._event_stack[0][name] = handler

    def pop_handlers(self):
        '''Pop the top level of event handlers off the stack.
        '''
        assert self._event_stack and 'No handlers pushed'

        del self._event_stack[0]

    def dispatch_event(self, event_type, *args):
        '''Dispatch a single event to the attached handlers.

        The event is propogated to all handlers from from the top of the stack
        until one returns `EVENT_HANDLED`.  This method should be used only by
        `EventDispatcher` implementors; applications should call
        the ``dispatch_events`` method.

        :Parameters:
            `event_type` : str
                Name of the event.
            `args` : sequence
                Arguments to pass to the event handler.

        '''
        assert event_type in self.event_types

        # Search handler stack for matching event handlers
        for frame in self._event_stack[:]:
            handler = frame.get(event_type, None)
            if handler:
                ret = handler(*args)
                if ret != EVENT_UNHANDLED:
                    return

        # Check instance for an event handler
        if hasattr(self, event_type):
            getattr(self, event_type)(*args)

    def event(self, *args):
        '''Function decorator for an event handler.

        Usage::

            win = window.Window()

            @win.event
            def on_resize(self, width, height):
                # ...

        or::

            @win.event('on_resize')
            def foo(self, width, height):
                # ...

        '''
        if len(args) == 0:                      # @window.event()
            def decorator(func):
                name = func.__name__
                setattr(self, name, func)
                return func
            return decorator
        elif inspect.isroutine(args[0]):        # @window.event
            func = args[0]
            name = func.__name__
            setattr(self, name, func)
            return args[0]
        elif type(args[0]) in (str, unicode):   # @window.event('on_resize')
            name = args[0]
            def decorator(func):
                setattr(self, name, func)
                return func
            return decorator
<commit_msg>Fix check for EVENT_HANDLED to be for any True value.<commit_after># ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2007 Alex Holkner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the pyglet nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

'''Event dispatch framework.

All objects that produce events in pyglet implement `EventDispatcher`,
providing a consistent interface for registering and manipulating event
handlers.  A commonly used event dispatcher is `pyglet.window.Window`.

Event types
===========

For each event dispatcher there is a set of events that it dispatches; these
correspond with the type of event handlers you can attach.  Event types are
identified by their name, for example, ''on_resize''.  If you are creating a
new class which implements `EventDispatcher`, you must call
`EventDispatcher.register_event_type` for each event type.

Attaching event handlers
========================

An event handler is simply a function or method.  You can attach an event
handler by setting the appropriate function on the instance::

    def on_resize(width, height):
        # ...
    dispatcher.on_resize = on_resize

There is also a convenience decorator that reduces typing::

    @dispatcher.event
    def on_resize(width, height):
        # ...

You may prefer to subclass and override the event handlers instead::

    class MyDispatcher(DispatcherClass):
        def on_resize(self, width, height):
            # ...

Event handler stack
===================

When attaching an event handler to a dispatcher using the above methods, it
replaces any existing handler (causing the original handler to no longer be
called).  Each dispatcher maintains a stack of event handlers, allowing you to
insert an event handler "above" the existing one rather than replacing it.

There are two main use cases for "pushing" event handlers:

* Temporarily intercepting the events coming from the dispatcher by pushing a
  custom set of handlers onto the dispatcher, then later "popping" them all
  off at once.
* Creating "chains" of event handlers, where the event propogates from the
  top-most (most recently added) handler to the bottom, until a handler
  takes care of it.

Use `EventDispatcher.push_handlers` to create a new level in the stack and
attach handlers to it.  You can push several handlers at once::

    dispatcher.push_handlers(on_resize, on_key_press)

If your function handlers have different names to the events they handle, use
keyword arguments::

    dispatcher.push_handlers(on_resize=my_resize,
                             on_key_press=my_key_press)

After an event handler has processed an event, it is passed on to the
next-lowest event handler, unless the handler returns `EVENT_HANDLED`, which
prevents further propogation.

To remove all handlers on the top stack level, use
`EventDispatcher.pop_handlers`.

Note that any handlers pushed onto the stack have precedence over the
handlers set directly on the instance (for example, using the methods
described in the previous section), regardless of when they were set.
For example, handler ``foo`` is called before handler ``bar`` in the following
example::

    dispatcher.push_handlers(on_resize=foo)
    dispatcher.on_resize = bar

Dispatching events
==================

pyglet uses a single-threaded model for all application code.  Event
handlers are only ever invoked as a result of calling
EventDispatcher.dispatch_events`.

It is up to the specific event dispatcher to queue relevant events until they
can be dispatched, at which point the handlers are called in the order the
events were originally generated.

This implies that your application runs with a main loop that continously
updates the application state and checks for new events::

    while True:
        dispatcher.dispatch_events()
        # ... additional per-frame processing

Not all event dispatchers require the call to ``dispatch_events``; check with
the particular class documentation.

'''

__docformat__ = 'restructuredtext'
__version__ = '$Id$'

import inspect

EVENT_HANDLED = True
EVENT_UNHANDLED = None

class EventException(Exception):
    '''An exception raised when an event handler could not be attached.
    '''
    pass

class EventDispatcher(object):
    '''Generic event dispatcher interface.

    See the module docstring for usage.
    '''
    # Placeholder empty stack; real stack is created only if needed
    _event_stack = ()

    @classmethod
    def register_event_type(cls, name):
        '''Register an event type with the dispatcher.

        Registering event types allows the dispatcher to validate event
        handler names as they are attached, and to search attached objects for
        suitable handlers.

        :Parameters:
            `name` : str
                Name of the event to register.

        '''
        if not hasattr(cls, 'event_types'):
            cls.event_types = []
        cls.event_types.append(name)
        return name

    def push_handlers(self, *args, **kwargs):
        '''Push a level onto the top of the handler stack, then attach zero or
        more event handlers.

        If keyword arguments are given, they name the event type to attach.
        Otherwise, a callable's `__name__` attribute will be used.  Any other
        object may also be specified, in which case it will be searched for
        callables with event names.
        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        # Place dict full of new handlers at beginning of stack
        self._event_stack.insert(0, {})
        self.set_handlers(*args, **kwargs)

    def set_handlers(self, *args, **kwargs):
        '''Attach one or more event handlers to the top level of the handler
        stack.

        See `push_handlers` for the accepted argument types.
        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        for object in args:
            if inspect.isroutine(object):
                # Single magically named function
                name = object.__name__
                if name not in self.event_types:
                    raise EventException('Unknown event "%s"' % name)
                self.set_handler(name, object)
            else:
                # Single instance with magically named methods
                for name, handler in inspect.getmembers(object):
                    if name in self.event_types:
                        self.set_handler(name, handler)
        for name, handler in kwargs.items():
            # Function for handling given event (no magic)
            if name not in self.event_types:
                raise EventException('Unknown event "%s"' % name)
            self.set_handler(name, handler)

    def set_handler(self, name, handler):
        '''Attach a single event handler.

        :Parameters:
            `name` : str
                Name of the event type to attach to.
            `handler` : callable
                Event handler to attach.

        '''
        # Create event stack if necessary
        if type(self._event_stack) is tuple:
            self._event_stack = [{}]

        self._event_stack[0][name] = handler

    def pop_handlers(self):
        '''Pop the top level of event handlers off the stack.
        '''
        assert self._event_stack and 'No handlers pushed'

        del self._event_stack[0]

    def dispatch_event(self, event_type, *args):
        '''Dispatch a single event to the attached handlers.

        The event is propogated to all handlers from from the top of the stack
        until one returns `EVENT_HANDLED`.  This method should be used only by
        `EventDispatcher` implementors; applications should call
        the ``dispatch_events`` method.

        :Parameters:
            `event_type` : str
                Name of the event.
            `args` : sequence
                Arguments to pass to the event handler.

        '''
        assert event_type in self.event_types

        # Search handler stack for matching event handlers
        for frame in self._event_stack[:]:
            handler = frame.get(event_type, None)
            if handler:
                if handler(*args):
                    return

        # Check instance for an event handler
        if hasattr(self, event_type):
            getattr(self, event_type)(*args)

    def event(self, *args):
        '''Function decorator for an event handler.

        Usage::

            win = window.Window()

            @win.event
            def on_resize(self, width, height):
                # ...

        or::

            @win.event('on_resize')
            def foo(self, width, height):
                # ...

        '''
        if len(args) == 0:                      # @window.event()
            def decorator(func):
                name = func.__name__
                setattr(self, name, func)
                return func
            return decorator
        elif inspect.isroutine(args[0]):        # @window.event
            func = args[0]
            name = func.__name__
            setattr(self, name, func)
            return args[0]
        elif type(args[0]) in (str, unicode):   # @window.event('on_resize')
            name = args[0]
            def decorator(func):
                setattr(self, name, func)
                return func
            return decorator
<|endoftext|>


<commit_before>import asyncio
import logging

from .protocol import LanguageServerProtocol

logger = logging.getLogger(__name__)

class Server:
    def __init__(self, protocol_cls):
        assert issubclass(protocol_cls, asyncio.Protocol)
        self.loop = asyncio.get_event_loop()

        self.lsp = protocol_cls(self)
        self.server = None

    def shutdown(self):
        self.server.close()
        # TODO: Gracefully shutdown event loops

    def start_tcp(self, host, port):
        self.server = self.loop.run_until_complete(
            self.loop.create_server(self.lsp, host, port)
        )
        self.loop.run_forever()

class LanguageServer(Server):
    def __init__(self):
        super().__init__(LanguageServerProtocol)

    def command(self, command_name):
        '''
        Registers new command (delegating to FeatureManager).

        Args:
            command_name(str): Name of the command to register
        '''
        return self.lsp.fm.command(self, command_name)

    def feature(self, *feature_names, **options):
        '''
        Registers one or more LSP features (delegating to FeatureManager).

        Args:
            *feature_names(tuple): One or more features to register
                NOTE: All possible LSP features are listed in lsp module
            **options(dict): Options for registered feature
                E.G. triggerCharacters=['.']
        '''
        return self.lsp.fm.feature(self, *feature_names, **options)

    def thread(self):
        return self.lsp.thread()
<commit_msg>Consume and write data using stdin/stdout<commit_after>import asyncio
import logging
import os
import sys
from re import findall

from . import IS_WIN
from .protocol import LanguageServerProtocol

logger = logging.getLogger(__name__)

async def aio_readline(loop, proxy):
    '''
    Read data from stdin in separate thread (asynchronously)
    '''
    while True:
        # Read line
        line = await loop.run_in_executor(None, sys.stdin.readline)

        if not line:
            continue

        # Extract content length from line
        try:
            content_length = int(findall(r'\b\d+\b', line)[0])
            logger.info(content_length)
        except:
            continue

        # Throw away empty lines
        while line and line.strip():
            line = await loop.run_in_executor(None, sys.stdin.readline)

        if not line:
            continue

        # Read body
        body = await loop.run_in_executor(None, sys.stdin.read, content_length)

        # Pass body to language server protocol
        if body:
            proxy(body.encode('utf-8'))

class StdOutTransportAdapter(asyncio.Transport):
    '''
    Protocol adapter which overrides write method.
    Write method sends data to stdout.
    '''

    def close(self):
        sys.stdout.buffer.close()

    def write(self, data):
        sys.stdout.buffer.write(data)
        sys.stdout.buffer.flush()

class Server:
    def __init__(self, protocol_cls):
        assert issubclass(protocol_cls, asyncio.Protocol)

        if IS_WIN:
            asyncio.set_event_loop(asyncio.ProactorEventLoop())

        self.loop = asyncio.get_event_loop()

        self.lsp = protocol_cls(self)
        self.server = None

    def shutdown(self):
        # self.server.close()
        # TODO: Gracefully shutdown event loops
        pass

    def start_tcp(self, host, port):
        self.server = self.loop.run_until_complete(
            self.loop.create_server(self.lsp, host, port)
        )
        self.loop.run_forever()

    def start_io(self):
        transport = StdOutTransportAdapter()
        self.lsp.connection_made(transport)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(aio_readline(loop, self.lsp.data_received))

class LanguageServer(Server):
    def __init__(self):
        super().__init__(LanguageServerProtocol)

    def command(self, command_name):
        '''
        Registers new command (delegating to FeatureManager).

        Args:
            command_name(str): Name of the command to register
        '''
        return self.lsp.fm.command(self, command_name)

    def feature(self, *feature_names, **options):
        '''
        Registers one or more LSP features (delegating to FeatureManager).

        Args:
            *feature_names(tuple): One or more features to register
                NOTE: All possible LSP features are listed in lsp module
            **options(dict): Options for registered feature
                E.G. triggerCharacters=['.']
        '''
        return self.lsp.fm.feature(self, *feature_names, **options)

    def thread(self):
        return self.lsp.thread()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from django.conf import settings as django_settings
from django.utils.translation import ugettext_lazy as _
from web.default_settings import DEFAULT_ENABLED_COLLECTIONS, DEFAULT_ENABLED_PAGES, DEFAULT_JAVASCRIPT_TRANSLATED_TERMS
from web.utils import tourldash
from cpro import models, forms, filters, collections, utils

SITE_NAME = u'Cinderella Producers β'
SITE_URL = 'http://cinderella.pro/'
SITE_IMAGE = 'cpro.png'
SITE_STATIC_URL = '//localhost:{}/'.format(django_settings.DEBUG_PORT) if django_settings.DEBUG else '//i.cinderella.pro/'
#SITE_STATIC_URL = '//i.cinderella.pro/'
GAME_NAME = 'IDOLM@STER Cinderella Girls Starlight Stage'
GAME_URL = 'https://itunes.apple.com/jp/app/idolmaster-cinderella-girls/id1016318735?l=en&mt=8'
GAME_DESCRIPTION = 'THE iDOLM@STER Cinderella Girls: Starlight Stage is a spin-off free-to-play rhythm game released for mobile devices.'
DISQUS_SHORTNAME = 'cinderellapro'
ACCOUNT_MODEL = models.Account
COLOR = '#4a86e8'

GOOGLE_ANALYTICS = 'UA-59453399-3'

GITHUB_REPOSITORY = ('SchoolIdolTomodachi', 'CinderellaProducers')
TWITTER_HANDLE = 'cinderella__pro'

HASHTAGS = ['imas', 'deresute', u'デレステ']

USER_COLORS = models.TYPES

GET_GLOBAL_CONTEXT = utils.globalContext

STATIC_FILES_VERSION = '4'

TOTAL_DONATORS = getattr(django_settings, 'TOTAL_DONATORS', 2) + 2
FAVORITE_CHARACTERS = getattr(django_settings, 'FAVORITE_CHARACTERS', None)
FAVORITE_CHARACTER_TO_URL = lambda link: '/idol/{pk}/{name}/'.format(pk=link.raw_value, name=tourldash(link.value))
FAVORITE_CHARACTER_NAME = _(u'{nth} Favorite Idol')

LATEST_NEWS = getattr(django_settings, 'LATEST_NEWS', 2)

EMPTY_IMAGE = 'cinderella.png'

JAVASCRIPT_TRANSLATED_TERMS = DEFAULT_JAVASCRIPT_TRANSLATED_TERMS
JAVASCRIPT_TRANSLATED_TERMS += [
    'Permalink',
    'Deleted',
]

ON_USER_EDITED = utils.onUserEdited
ON_PREFERENCES_EDITED = utils.onPreferencesEdited

DONATORS_STATUS_CHOICES = models.DONATORS_STATUS_CHOICES

ENABLED_COLLECTIONS = DEFAULT_ENABLED_COLLECTIONS

ACTIVITY_TAGS = [
    ('cards', _('New Cards')),
    ('event', _('Event')),
    ('live', _('Live')),
    ('comedy', _('Comedy')),
    ('introduction', _('Introduce yourself')),
    ('idols', _('Idols')),
    ('fanart', _('Fanart')),
    ('cosplay', _('Cosplay')),
    ('anime', _('Anime')),
    ('community', _('Community')),
    ('unrelated', _('Not about IDOLM@STER')),
]

ENABLED_COLLECTIONS['activity']['add']['before_save'] = collections.activitiesBeforeSave
ENABLED_COLLECTIONS['activity']['edit']['before_save'] = collections.activitiesBeforeSave

ENABLED_COLLECTIONS['account']['list']['distinct'] = True

ENABLED_COLLECTIONS['account']['add']['form_class'] = collections.getAccountForm
ENABLED_COLLECTIONS['account']['add']['back_to_list_button'] = False
ENABLED_COLLECTIONS['account']['add']['after_save'] = collections.addAccountAfterSave
ENABLED_COLLECTIONS['account']['edit']['form_class'] = forms.AccountFormAdvanced

ENABLED_COLLECTIONS['account']['add']['otherbuttons_template'] = 'include/advancedButton'
ENABLED_COLLECTIONS['account']['add']['extra_context'] = collections.modAccountExtraContext
ENABLED_COLLECTIONS['account']['edit']['extra_context'] = collections.modAccountExtraContext
ENABLED_COLLECTIONS['account']['add']['after_template'] = 'include/accountJSstarter'
ENABLED_COLLECTIONS['account']['edit']['after_template'] = 'include/accountJSstarter'
ENABLED_COLLECTIONS['account']['add']['js_files'] = ENABLED_COLLECTIONS['account']['add'].get('js_files', []) + ['mod_account']
ENABLED_COLLECTIONS['account']['edit']['js_files'] = ENABLED_COLLECTIONS['account']['edit'].get('js_files', []) + ['mod_account']

ENABLED_COLLECTIONS['account']['add']['redirect_after_add'] = collections.redirectAfterAddAccount

ENABLED_COLLECTIONS['account']['list']['before_template'] = 'include/beforeLeaderboard'
ENABLED_COLLECTIONS['account']['list']['default_ordering'] = '-level'
ENABLED_COLLECTIONS['account']['list']['filter_form'] = forms.FilterAccounts
ENABLED_COLLECTIONS['account']['list']['filter_queryset'] = filters.filterAccounts
ENABLED_COLLECTIONS['account']['list']['js_files'] = ENABLED_COLLECTIONS['account']['list'].get('js_files', []) + ['leaderboard']
ENABLED_COLLECTIONS['account']['list']['extra_context'] = collections.leaderboardExtraContext

ENABLED_COLLECTIONS['user']['item']['extra_context'] = collections.profileGetAccountTabs
ENABLED_COLLECTIONS['user']['item']['js_files'] = ENABLED_COLLECTIONS['user']['item'].get('js_files', []) + ['profile_account_tabs']

ENABLED_COLLECTIONS['card'] = {
    'queryset': models.Card.objects.all(),
    'title': _('Card'),
    'plural_title': _('Cards'),
    'icon': 'cards',
    'list': {
        'default_ordering': '-release_date',
        'full_width': True,
        'ajax_pagination_callback': 'updateCards',
        'js_files': ['cards'],
        'filter_form': forms.FilterCards,
        'filter_queryset': filters.filterCards,
        'before_template': 'include/beforeCards',
        'after_template': 'include/afterCards',
        'extra_context': collections.cardsExtraContext,
    },
    'item': {
        'ajax_callback': 'updateCardsAndOwnedCards',
        'js_files': ['cards', 'collection'],
        'filter_queryset': filters.filterCard,
        'extra_context': collections.cardExtraContext,
    },
    'add': {
        'form_class': forms.CardForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.CardForm,
        'multipart': True,
        'staff_required': True,
    },
}

ENABLED_COLLECTIONS['ownedcard'] = {
    'queryset': models.OwnedCard.objects.all().select_related('card'),
    'title': _('Card'),
    'plural_title': _('Cards'),
    'icon': 'album',
    'navbar_link': False,
    'list': {
        'default_ordering': '-card__i_rarity,-awakened,-card__release_date,card__idol__i_type',
        'per_line': 6,
        'page_size': 48,
        'filter_queryset': filters.filterOwnedCards,
        'col_break': 'xs',
        'foreach_items': collections.foreachOwnedCard,
        'filter_form': forms.FilterOwnedCards,
        'js_files': ['ownedcards'],
        'ajax_pagination_callback': 'updateOwnedCards',
    },
    'edit': {
        'form_class': forms.EditOwnedCardForm,
        'filter_queryset': lambda q, p, r: q.select_related('account'), # Used when checking if the center has been updated
        'redirect_after_edit': collections.ownedCardRedirectAfter,
        'redirect_after_delete': collections.ownedCardRedirectAfter,
        'allow_delete': True,
        'back_to_list_button': False,
        'js_files': ['edit_ownedcard'],
    },
    'item': {
        'comments_enabled': False,
        'js_files': ['ownedcards'],
    },
}

ENABLED_COLLECTIONS['idol'] = {
    'queryset': models.Idol.objects.all(),
    'title': _('Idol'),
    'plural_title': _('Idols'),
    'icon': 'idolized',
    'list': {
        'default_ordering': 'name',
        'per_line': 4,
        'ajax_pagination_callback': 'ajaxModals',
        'filter_form': forms.FilterIdols,
        'filter_queryset': filters.filterIdols,
        'extra_context': collections.idolsExtraContext,
        'js_files': ['idols'],
    },
    'item': {
        'template': 'idolInfo',
        'js_files': ['idolInfo'],
    },
    'add': {
        'form_class': forms.IdolForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.IdolForm,
        'multipart': True,
        'staff_required': True,
    },
}

ENABLED_COLLECTIONS['event'] = {
    'queryset': models.Event.objects.all(),
    'title': _('Event'),
    'plural_title': _('Events'),
    'icon': 'event',
    'list': {
        'default_ordering': '-end',
        'per_line': 1,
        'filter_form': forms.FilterEvents,
        'filter_queryset': filters.filterEvents,
    },
    'item': {
        'template': 'eventInfo',
        'js_files': ['bower/countdown/dest/jquery.countdown.min', 'event'],
    },
    'add': {
        'form_class': forms.EventForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.EventForm,
        'multipart': True,
        'staff_required': True,
    },
}

for name, collection in ENABLED_COLLECTIONS.items():
    if name not in ['activity', 'ownedcard'] and 'list' in collection:
        collection['list']['authentication_required'] = True

ENABLED_PAGES = DEFAULT_ENABLED_PAGES

ENABLED_PAGES['index']['custom'] = True
ENABLED_PAGES['signup']['custom'] = True

ENABLED_PAGES['cardstat'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['cardcollection'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['addcard'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['account_about'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('account', '\d+'),
    ],
}
<commit_msg>Add a tag for room decorations<commit_after># -*- coding: utf-8 -*-
from django.conf import settings as django_settings
from django.utils.translation import ugettext_lazy as _
from web.default_settings import DEFAULT_ENABLED_COLLECTIONS, DEFAULT_ENABLED_PAGES, DEFAULT_JAVASCRIPT_TRANSLATED_TERMS
from web.utils import tourldash
from cpro import models, forms, filters, collections, utils

SITE_NAME = u'Cinderella Producers β'
SITE_URL = 'http://cinderella.pro/'
SITE_IMAGE = 'cpro.png'
SITE_STATIC_URL = '//localhost:{}/'.format(django_settings.DEBUG_PORT) if django_settings.DEBUG else '//i.cinderella.pro/'
#SITE_STATIC_URL = '//i.cinderella.pro/'
GAME_NAME = 'IDOLM@STER Cinderella Girls Starlight Stage'
GAME_URL = 'https://itunes.apple.com/jp/app/idolmaster-cinderella-girls/id1016318735?l=en&mt=8'
GAME_DESCRIPTION = 'THE iDOLM@STER Cinderella Girls: Starlight Stage is a spin-off free-to-play rhythm game released for mobile devices.'
DISQUS_SHORTNAME = 'cinderellapro'
ACCOUNT_MODEL = models.Account
COLOR = '#4a86e8'

GOOGLE_ANALYTICS = 'UA-59453399-3'

GITHUB_REPOSITORY = ('SchoolIdolTomodachi', 'CinderellaProducers')
TWITTER_HANDLE = 'cinderella__pro'

HASHTAGS = ['imas', 'deresute', u'デレステ']

USER_COLORS = models.TYPES

GET_GLOBAL_CONTEXT = utils.globalContext

STATIC_FILES_VERSION = '4'

TOTAL_DONATORS = getattr(django_settings, 'TOTAL_DONATORS', 2) + 2
FAVORITE_CHARACTERS = getattr(django_settings, 'FAVORITE_CHARACTERS', None)
FAVORITE_CHARACTER_TO_URL = lambda link: '/idol/{pk}/{name}/'.format(pk=link.raw_value, name=tourldash(link.value))
FAVORITE_CHARACTER_NAME = _(u'{nth} Favorite Idol')

LATEST_NEWS = getattr(django_settings, 'LATEST_NEWS', 2)

EMPTY_IMAGE = 'cinderella.png'

JAVASCRIPT_TRANSLATED_TERMS = DEFAULT_JAVASCRIPT_TRANSLATED_TERMS
JAVASCRIPT_TRANSLATED_TERMS += [
    'Permalink',
    'Deleted',
]

ON_USER_EDITED = utils.onUserEdited
ON_PREFERENCES_EDITED = utils.onPreferencesEdited

DONATORS_STATUS_CHOICES = models.DONATORS_STATUS_CHOICES

ENABLED_COLLECTIONS = DEFAULT_ENABLED_COLLECTIONS

ACTIVITY_TAGS = [
    ('cards', _('New Cards')),
    ('event', _('Event')),
    ('live', _('Live')),
    ('comedy', _('Comedy')),
    ('room', _('Room Decoration')),
    ('introduction', _('Introduce yourself')),
    ('idols', _('Idols')),
    ('fanart', _('Fanart')),
    ('cosplay', _('Cosplay')),
    ('anime', _('Anime')),
    ('community', _('Community')),
    ('unrelated', _('Not about IDOLM@STER')),
]

ENABLED_COLLECTIONS['activity']['add']['before_save'] = collections.activitiesBeforeSave
ENABLED_COLLECTIONS['activity']['edit']['before_save'] = collections.activitiesBeforeSave

ENABLED_COLLECTIONS['account']['list']['distinct'] = True

ENABLED_COLLECTIONS['account']['add']['form_class'] = collections.getAccountForm
ENABLED_COLLECTIONS['account']['add']['back_to_list_button'] = False
ENABLED_COLLECTIONS['account']['add']['after_save'] = collections.addAccountAfterSave
ENABLED_COLLECTIONS['account']['edit']['form_class'] = forms.AccountFormAdvanced

ENABLED_COLLECTIONS['account']['add']['otherbuttons_template'] = 'include/advancedButton'
ENABLED_COLLECTIONS['account']['add']['extra_context'] = collections.modAccountExtraContext
ENABLED_COLLECTIONS['account']['edit']['extra_context'] = collections.modAccountExtraContext
ENABLED_COLLECTIONS['account']['add']['after_template'] = 'include/accountJSstarter'
ENABLED_COLLECTIONS['account']['edit']['after_template'] = 'include/accountJSstarter'
ENABLED_COLLECTIONS['account']['add']['js_files'] = ENABLED_COLLECTIONS['account']['add'].get('js_files', []) + ['mod_account']
ENABLED_COLLECTIONS['account']['edit']['js_files'] = ENABLED_COLLECTIONS['account']['edit'].get('js_files', []) + ['mod_account']

ENABLED_COLLECTIONS['account']['add']['redirect_after_add'] = collections.redirectAfterAddAccount

ENABLED_COLLECTIONS['account']['list']['before_template'] = 'include/beforeLeaderboard'
ENABLED_COLLECTIONS['account']['list']['default_ordering'] = '-level'
ENABLED_COLLECTIONS['account']['list']['filter_form'] = forms.FilterAccounts
ENABLED_COLLECTIONS['account']['list']['filter_queryset'] = filters.filterAccounts
ENABLED_COLLECTIONS['account']['list']['js_files'] = ENABLED_COLLECTIONS['account']['list'].get('js_files', []) + ['leaderboard']
ENABLED_COLLECTIONS['account']['list']['extra_context'] = collections.leaderboardExtraContext

ENABLED_COLLECTIONS['user']['item']['extra_context'] = collections.profileGetAccountTabs
ENABLED_COLLECTIONS['user']['item']['js_files'] = ENABLED_COLLECTIONS['user']['item'].get('js_files', []) + ['profile_account_tabs']

ENABLED_COLLECTIONS['card'] = {
    'queryset': models.Card.objects.all(),
    'title': _('Card'),
    'plural_title': _('Cards'),
    'icon': 'cards',
    'list': {
        'default_ordering': '-release_date',
        'full_width': True,
        'ajax_pagination_callback': 'updateCards',
        'js_files': ['cards'],
        'filter_form': forms.FilterCards,
        'filter_queryset': filters.filterCards,
        'before_template': 'include/beforeCards',
        'after_template': 'include/afterCards',
        'extra_context': collections.cardsExtraContext,
    },
    'item': {
        'ajax_callback': 'updateCardsAndOwnedCards',
        'js_files': ['cards', 'collection'],
        'filter_queryset': filters.filterCard,
        'extra_context': collections.cardExtraContext,
    },
    'add': {
        'form_class': forms.CardForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.CardForm,
        'multipart': True,
        'staff_required': True,
    },
}

ENABLED_COLLECTIONS['ownedcard'] = {
    'queryset': models.OwnedCard.objects.all().select_related('card'),
    'title': _('Card'),
    'plural_title': _('Cards'),
    'icon': 'album',
    'navbar_link': False,
    'list': {
        'default_ordering': '-card__i_rarity,-awakened,-card__release_date,card__idol__i_type',
        'per_line': 6,
        'page_size': 48,
        'filter_queryset': filters.filterOwnedCards,
        'col_break': 'xs',
        'foreach_items': collections.foreachOwnedCard,
        'filter_form': forms.FilterOwnedCards,
        'js_files': ['ownedcards'],
        'ajax_pagination_callback': 'updateOwnedCards',
    },
    'edit': {
        'form_class': forms.EditOwnedCardForm,
        'filter_queryset': lambda q, p, r: q.select_related('account'), # Used when checking if the center has been updated
        'redirect_after_edit': collections.ownedCardRedirectAfter,
        'redirect_after_delete': collections.ownedCardRedirectAfter,
        'allow_delete': True,
        'back_to_list_button': False,
        'js_files': ['edit_ownedcard'],
    },
    'item': {
        'comments_enabled': False,
        'js_files': ['ownedcards'],
    },
}

ENABLED_COLLECTIONS['idol'] = {
    'queryset': models.Idol.objects.all(),
    'title': _('Idol'),
    'plural_title': _('Idols'),
    'icon': 'idolized',
    'list': {
        'default_ordering': 'name',
        'per_line': 4,
        'ajax_pagination_callback': 'ajaxModals',
        'filter_form': forms.FilterIdols,
        'filter_queryset': filters.filterIdols,
        'extra_context': collections.idolsExtraContext,
        'js_files': ['idols'],
    },
    'item': {
        'template': 'idolInfo',
        'js_files': ['idolInfo'],
    },
    'add': {
        'form_class': forms.IdolForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.IdolForm,
        'multipart': True,
        'staff_required': True,
    },
}

ENABLED_COLLECTIONS['event'] = {
    'queryset': models.Event.objects.all(),
    'title': _('Event'),
    'plural_title': _('Events'),
    'icon': 'event',
    'list': {
        'default_ordering': '-end',
        'per_line': 1,
        'filter_form': forms.FilterEvents,
        'filter_queryset': filters.filterEvents,
    },
    'item': {
        'template': 'eventInfo',
        'js_files': ['bower/countdown/dest/jquery.countdown.min', 'event'],
    },
    'add': {
        'form_class': forms.EventForm,
        'multipart': True,
        'staff_required': True,
    },
    'edit': {
        'form_class': forms.EventForm,
        'multipart': True,
        'staff_required': True,
    },
}

for name, collection in ENABLED_COLLECTIONS.items():
    if name not in ['activity', 'ownedcard'] and 'list' in collection:
        collection['list']['authentication_required'] = True

ENABLED_PAGES = DEFAULT_ENABLED_PAGES

ENABLED_PAGES['index']['custom'] = True
ENABLED_PAGES['signup']['custom'] = True

ENABLED_PAGES['cardstat'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['cardcollection'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['addcard'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('card', '\d+'),
    ],
}

ENABLED_PAGES['account_about'] = {
    'ajax': True,
    'navbar_link': False,
    'url_variables':  [
        ('account', '\d+'),
    ],
}
<|endoftext|>


<commit_before>#!/usr/bin/env python

__author__ = 'Stephen P. Henrie'
__license__ = 'Apache 2.0'

from flask import Flask, request, jsonify
from gevent.wsgi import WSGIServer
import inspect, json, simplejson, collections, ast

from pyon.public import IonObject, Container, ProcessRPCClient
from pyon.core.exception import NotFound, Inconsistent
from pyon.core.registry import get_message_class_in_parm_type, getextends

from interface.services.coi.iservice_gateway_service import BaseServiceGatewayService
from interface.services.coi.iresource_registry_service import IResourceRegistryService, ResourceRegistryServiceProcessClient

#Initialize the flask app
app = Flask(__name__)

#Retain a module level reference to the service class for use with Process RPC calls below
service_gateway_instance = None

DEFAULT_WEB_SERVER_HOSTNAME = ""
DEFAULT_WEB_SERVER_PORT = 5000

#This class is used to manage the WSGI/Flask server as an ION process - and as a process endpoint for ION RPC calls
class ServiceGatewayService(BaseServiceGatewayService):

    """
	The Service Gateway Service is the service that uses a gevent web server and Flask to bridge HTTP requests to AMQP RPC ION process service calls.

    """

    def on_init(self):

        #defaults
        self.http_server = None
        self.server_hostname = DEFAULT_WEB_SERVER_HOSTNAME
        self.server_port = DEFAULT_WEB_SERVER_PORT
        self.web_server_enabled = True
        self.logging = None

        #retain a pointer to this object for use in ProcessRPC calls
        global service_gateway_instance
        service_gateway_instance = self

        #get configuration settings if specified
        if 'web_server' in self.CFG:
            web_server_cfg = self.CFG['web_server']
            if web_server_cfg is not None:
                if 'hostname' in web_server_cfg:
                    self.server_hostname = web_server_cfg['hostname']
                if 'port' in web_server_cfg:
                    self.server_port = web_server_cfg['port']
                if 'enabled' in web_server_cfg:
                    self.web_server_enabled = web_server_cfg['enabled']
                if 'log' in web_server_cfg:
                    self.logging = web_server_cfg['log']

        #need to figure out how to redirect HTTP logging to a file
        if self.web_server_enabled:
            self.start_service(self.server_hostname,self.server_port)

    def on_quit(self):
        self.stop_service()

    def start_service(self, hostname=DEFAULT_WEB_SERVER_HOSTNAME, port=DEFAULT_WEB_SERVER_PORT):
        """Responsible for starting the gevent based web server."""

        if self.http_server is not None:
            self.stop_service()

        self.http_server = WSGIServer((hostname, port), app, log=self.logging)
        self.http_server.start()

        return True

    def stop_service(self):
        """Responsible for stopping the gevent based web server."""
        if self.http_server is not None:
            self.http_server.stop()
        return True

# This is a service operation for handling generic service operation calls with arguments passed as query string parameters; like this:
# http://hostname:port/ion-service/resource_registry/find_resources?restype=BankAccount&id_only=False
# Shows how to handle POST requests with the parameters passed in posted JSON object, though has both since it is easier to use a browser URL GET than a POST by hand.

# An example post of json data using wget
# wget -o out.txt --post-data 'payload={"serviceRequest": { "serviceName": "resource_registry", "serviceOp": "find_resources",
# "params": { "restype": "BankAccount", "lcstate": "", "name": "", "id_only": true } } }' http://localhost:5000/ion-service/resource_registry/find_resources
#
# Probably should make this service smarter to respond to the mime type in the request data ( ie. json vs text )
#
@app.route('/ion-service/<service_name>/<operation>', methods=['GET','POST'])
def process_gateway_request(service_name, operation):

    try:
        #Retrieve service definition
        from pyon.core.bootstrap import service_registry
        # MM: Note: service_registry can do more now
        target_service = service_registry.get_service_base(service_name)

        if not target_service:
            raise NotFound("Target service name not found in the URL")

        if operation == '':
            raise NotFound("Service operation not specified in the URL")

        #Find the concrete client class for making the RPC calls.
        target_client = None
        for name, cls in inspect.getmembers(inspect.getmodule(target_service),inspect.isclass):
            if issubclass(cls, ProcessRPCClient) and not name.endswith('ProcessRPCClient'):
                target_client = cls
                break

        if not target_client:
            raise NotFound("Service operation not correctly specified in the URL")

        jsonParms = None
        if request.method == "POST":
            payload = request.form['payload']
            #debug only
            #payload = '{"serviceRequest": { "serviceName": "resource_registry", "serviceOp": "find_resources", "params": { "restype": "BankAccount", "lcstate": "", "name": "", "id_only": false } } }'
            jsonParms = json.loads(payload)

            if jsonParms['serviceRequest']['serviceName'] != target_service.name:
                 raise Inconsistent("Target service name in the JSON request (%s) does not match service name in URL (%s)" % (str(jsonParms['serviceRequest']['serviceName']), target_service.name ) )

            if jsonParms['serviceRequest']['serviceOp'] != operation:
                 raise Inconsistent("Target service operation in the JSON request (%s) does not match service name in URL (%s)" % ( str(jsonParms['serviceRequest']['serviceOp']), operation ) )

        #Build parameter list - operation parameters must be in the proper order. Should replace this once unordered method invocation is
        # allowed in the container, but good enough for demonstration purposes.
        parm_list = {}
        method_args = inspect.getargspec(getattr(target_client,operation))
        for arg in method_args[0]:
            if arg == 'self' or arg == 'headers': continue # skip self and headers from being set

            if not jsonParms:
                if request.args.has_key(arg):
                    parm_type = get_message_class_in_parm_type(service_name, operation, arg)
                    if parm_type == 'str':
                        parm_list[arg] = convert_unicode(request.args[arg])
                    else:
                        parm_list[arg] = ast.literal_eval(convert_unicode(request.args[arg]))
            else:
                if jsonParms['serviceRequest']['params'].has_key(arg):
                    if isinstance(jsonParms['serviceRequest']['params'][arg], list):  #This if handles ION objects as a 2 element list: [Object Type, { field1: val1, ...}]
                        # For some reason, UNICODE strings are not supported with ION objects
                        ion_object_name = convert_unicode(jsonParms['serviceRequest']['params'][arg][0])
                        object_parms = convert_unicode(jsonParms['serviceRequest']['params'][arg][1])

                        new_obj = IonObject(ion_object_name)
                        #Iterate over the parameters to add to object; have to do this instead
                        #of passing a dict to get around restrictions in object creation on setting _id, _rev params
                        for parm in object_parms:
                            setattr(new_obj, parm, object_parms.get(parm))

                        new_obj._validate() # verify that all of the object fields were set with proper types
                        parm_list[arg] = new_obj
                    else:  # The else branch is for simple types ( non-ION objects )
                        parm_list[arg] = convert_unicode(jsonParms['serviceRequest']['params'][arg])

        client = target_client(node=Container.instance.node, process=service_gateway_instance)
        methodToCall = getattr(client, operation)
        result = methodToCall(**parm_list)

        ret = simplejson.dumps(result, default=ion_object_encoder)

    except Exception, e:
        ret =  "Error: %s" % e.message

    return jsonify(data=ret)

def ion_object_encoder(obj):
    return obj.__dict__

def convert_unicode(data):
    if isinstance(data, unicode):
        return str(data)
    elif isinstance(data, collections.Mapping):
        return dict(map(convert_unicode, data.iteritems()))
    elif isinstance(data, collections.Iterable):
        return type(data)(map(convert_unicode, data))
    else:
        return data

# This service method returns the list of registered resource objects sorted alphabetically. Optional query
# string parameter will filter by extended type  i.e. type=InformationResource. All registered objects
# will be returned if not filtered
#
# Examples:
# http://hostname:port/ion-service/list_resource_types
# http://hostname:port/ion-service/list_resource_types?type=InformationResource
# http://hostname:port/ion-service/list_resource_types?type=TaskableResource
#
@app.route('/ion-service/list_resource_types', methods=['GET','POST'])
def list_resource_types():

    try:
        #Look to see if a specific resource type has been specified - if not default to all
        if request.args.has_key('type'):
            resultSet = set(getextends(request.args['type'])) if getextends(request.args['type']) is not None else set()
        else:
            type_list = getextends('Resource')
            resultSet = set(type_list)

        ret_list = []
        for res in sorted(resultSet):
            ret_list.append(res)

        return jsonify(data=ret_list)

    except Exception, e:
        ret =  "Error: %s" % e.message
        return jsonify(data=ret)

#Returns a json object for a specified resource type with all default values.
@app.route('/ion-service/resource_type_schema/<resource_type>')
def get_resource_schema(resource_type):

    try:
        ion_object_name = convert_unicode(resource_type)
        ret_obj = IonObject(ion_object_name, {})

        # If it's an op input param or response message object.
        # Walk param list instantiating any params that were marked None as default.
        if hasattr(ret_obj, "_svc_name"):
            schema = ret_obj._schema
            for field in ret_obj._schema:
                if schema[field]["default"] is None:
                    try:
                        value = IonObject(schema[field]["type"], {})
                    except NotFound:
                        # TODO
                        # Some other non-IonObject type.  Just use None as default for now.
                        value = None
                    setattr(ret_obj, field, value)

        ret = simplejson.dumps(ret_obj, default=ion_object_encoder)

    except Exception, e:
        ret =  "Error: %s" % e.message

    return jsonify(data=ret)

#More RESTfull examples...should probably not use but here for example reference

#This example calls the resource registry with an id passed in as part of the URL
#http://hostname:port/ion-service/resource/c1b6fa6aadbd4eb696a9407a39adbdc8
@app.route('/ion-service/rest/resource/<resource_id>')
def get_resource(resource_id):

    ret = None
    client = ResourceRegistryServiceProcessClient(node=Container.instance.node, process=service_gateway_instance)
    if resource_id != '':
        try:
            result = client.read(resource_id)
            if not result:
                raise NotFound("No resource found for id: %s " % resource_id)

            ret = simplejson.dumps(result, default=ion_object_encoder)

        except Exception, e:
            ret =  "Error: %s" % e

    return jsonify(data=ret)

#Example operation to return a list of resources of a specific type like
#http://hostname:port/ion-service/find_resources/BankAccount
@app.route('/ion-service/rest/find_resources/<resource_type>')
def list_resources_by_type(resource_type):

    ret = None

    client = ResourceRegistryServiceProcessClient(node=Container.instance.node, process=service_gateway_instance)
    try:
        res_list,_ = client.find_resources(restype=resource_type )
        result = []
        for res in res_list:
            result.append(res)

        ret = simplejson.dumps(result, default=ion_object_encoder)
    except Exception, e:
        ret =  "Error: %s" % e

    return jsonify(data=ret)

#Example restful call to a client function for another service like
#http://hostname:port/ion-service/run_bank_client
@app.route('/ion-service/run_bank_client')
def create_accounts():
    from examples.bank.bank_client import run_client
    run_client(Container.instance, process=service_gateway_instance)
    return list_resources_by_type("BankAccount")

<commit_msg>Added recursive processing for sub object values<commit_after>#!/usr/bin/env python

__author__ = 'Stephen P. Henrie'
__license__ = 'Apache 2.0'

from flask import Flask, request, jsonify
from gevent.wsgi import WSGIServer
import inspect, json, simplejson, collections, ast

from pyon.public import IonObject, Container, ProcessRPCClient
from pyon.core.exception import NotFound, Inconsistent
from pyon.core.registry import get_message_class_in_parm_type, getextends

from interface.services.coi.iservice_gateway_service import BaseServiceGatewayService
from interface.services.coi.iresource_registry_service import IResourceRegistryService, ResourceRegistryServiceProcessClient

#Initialize the flask app
app = Flask(__name__)

#Retain a module level reference to the service class for use with Process RPC calls below
service_gateway_instance = None

DEFAULT_WEB_SERVER_HOSTNAME = ""
DEFAULT_WEB_SERVER_PORT = 5000

#This class is used to manage the WSGI/Flask server as an ION process - and as a process endpoint for ION RPC calls
class ServiceGatewayService(BaseServiceGatewayService):

    """
	The Service Gateway Service is the service that uses a gevent web server and Flask to bridge HTTP requests to AMQP RPC ION process service calls.

    """

    def on_init(self):

        #defaults
        self.http_server = None
        self.server_hostname = DEFAULT_WEB_SERVER_HOSTNAME
        self.server_port = DEFAULT_WEB_SERVER_PORT
        self.web_server_enabled = True
        self.logging = None

        #retain a pointer to this object for use in ProcessRPC calls
        global service_gateway_instance
        service_gateway_instance = self

        #get configuration settings if specified
        if 'web_server' in self.CFG:
            web_server_cfg = self.CFG['web_server']
            if web_server_cfg is not None:
                if 'hostname' in web_server_cfg:
                    self.server_hostname = web_server_cfg['hostname']
                if 'port' in web_server_cfg:
                    self.server_port = web_server_cfg['port']
                if 'enabled' in web_server_cfg:
                    self.web_server_enabled = web_server_cfg['enabled']
                if 'log' in web_server_cfg:
                    self.logging = web_server_cfg['log']

        #need to figure out how to redirect HTTP logging to a file
        if self.web_server_enabled:
            self.start_service(self.server_hostname,self.server_port)

    def on_quit(self):
        self.stop_service()

    def start_service(self, hostname=DEFAULT_WEB_SERVER_HOSTNAME, port=DEFAULT_WEB_SERVER_PORT):
        """Responsible for starting the gevent based web server."""

        if self.http_server is not None:
            self.stop_service()

        self.http_server = WSGIServer((hostname, port), app, log=self.logging)
        self.http_server.start()

        return True

    def stop_service(self):
        """Responsible for stopping the gevent based web server."""
        if self.http_server is not None:
            self.http_server.stop()
        return True

# This is a service operation for handling generic service operation calls with arguments passed as query string parameters; like this:
# http://hostname:port/ion-service/resource_registry/find_resources?restype=BankAccount&id_only=False
# Shows how to handle POST requests with the parameters passed in posted JSON object, though has both since it is easier to use a browser URL GET than a POST by hand.

# An example post of json data using wget
# wget -o out.txt --post-data 'payload={"serviceRequest": { "serviceName": "resource_registry", "serviceOp": "find_resources",
# "params": { "restype": "BankAccount", "lcstate": "", "name": "", "id_only": true } } }' http://localhost:5000/ion-service/resource_registry/find_resources
#
# Probably should make this service smarter to respond to the mime type in the request data ( ie. json vs text )
#
@app.route('/ion-service/<service_name>/<operation>', methods=['GET','POST'])
def process_gateway_request(service_name, operation):

    try:
        #Retrieve service definition
        from pyon.core.bootstrap import service_registry
        # MM: Note: service_registry can do more now
        target_service = service_registry.get_service_base(service_name)

        if not target_service:
            raise NotFound("Target service name not found in the URL")

        if operation == '':
            raise NotFound("Service operation not specified in the URL")

        #Find the concrete client class for making the RPC calls.
        target_client = None
        for name, cls in inspect.getmembers(inspect.getmodule(target_service),inspect.isclass):
            if issubclass(cls, ProcessRPCClient) and not name.endswith('ProcessRPCClient'):
                target_client = cls
                break

        if not target_client:
            raise NotFound("Service operation not correctly specified in the URL")

        jsonParms = None
        if request.method == "POST":
            payload = request.form['payload']
            #debug only
            #payload = '{"serviceRequest": { "serviceName": "resource_registry", "serviceOp": "find_resources", "params": { "restype": "BankAccount", "lcstate": "", "name": "", "id_only": false } } }'
            jsonParms = json.loads(payload)

            if jsonParms['serviceRequest']['serviceName'] != target_service.name:
                 raise Inconsistent("Target service name in the JSON request (%s) does not match service name in URL (%s)" % (str(jsonParms['serviceRequest']['serviceName']), target_service.name ) )

            if jsonParms['serviceRequest']['serviceOp'] != operation:
                 raise Inconsistent("Target service operation in the JSON request (%s) does not match service name in URL (%s)" % ( str(jsonParms['serviceRequest']['serviceOp']), operation ) )

        #Build parameter list - operation parameters must be in the proper order. Should replace this once unordered method invocation is
        # allowed in the container, but good enough for demonstration purposes.
        parm_list = {}
        method_args = inspect.getargspec(getattr(target_client,operation))
        for arg in method_args[0]:
            if arg == 'self' or arg == 'headers': continue # skip self and headers from being set

            if not jsonParms:
                if request.args.has_key(arg):
                    parm_type = get_message_class_in_parm_type(service_name, operation, arg)
                    if parm_type == 'str':
                        parm_list[arg] = convert_unicode(request.args[arg])
                    else:
                        parm_list[arg] = ast.literal_eval(convert_unicode(request.args[arg]))
            else:
                if jsonParms['serviceRequest']['params'].has_key(arg):
                    if isinstance(jsonParms['serviceRequest']['params'][arg], list):  #This if handles ION objects as a 2 element list: [Object Type, { field1: val1, ...}]
                        # For some reason, UNICODE strings are not supported with ION objects
                        ion_object_name = convert_unicode(jsonParms['serviceRequest']['params'][arg][0])
                        object_parms = convert_unicode(jsonParms['serviceRequest']['params'][arg][1])

                        new_obj = IonObject(ion_object_name)
                        #Iterate over the parameters to add to object; have to do this instead
                        #of passing a dict to get around restrictions in object creation on setting _id, _rev params
                        for parm in object_parms:
                            set_object_field(new_obj, parm, object_parms.get(parm))

                        new_obj._validate() # verify that all of the object fields were set with proper types
                        parm_list[arg] = new_obj
                    else:  # The else branch is for simple types ( non-ION objects )
                        parm_list[arg] = convert_unicode(jsonParms['serviceRequest']['params'][arg])

        client = target_client(node=Container.instance.node, process=service_gateway_instance)
        methodToCall = getattr(client, operation)
        result = methodToCall(**parm_list)

        ret = simplejson.dumps(result, default=ion_object_encoder)

    except Exception, e:
        ret =  "Error: %s" % e.message

    return jsonify(data=ret)

#Use this function internally to recursively set sub object field values
def set_object_field(obj, field, field_val):
    if isinstance(field_val,dict):
        sub_obj = getattr(obj,field)
        for sub_field in field_val:
            set_object_field(sub_obj, sub_field, field_val.get(sub_field))
    else:
        setattr(obj, field, field_val)

#Used by json encoder
def ion_object_encoder(obj):
    return obj.__dict__

#Used to recursively convert unicode in JSON structures into proper data structures
def convert_unicode(data):
    if isinstance(data, unicode):
        return str(data)
    elif isinstance(data, collections.Mapping):
        return dict(map(convert_unicode, data.iteritems()))
    elif isinstance(data, collections.Iterable):
        return type(data)(map(convert_unicode, data))
    else:
        return data

# This service method returns the list of registered resource objects sorted alphabetically. Optional query
# string parameter will filter by extended type  i.e. type=InformationResource. All registered objects
# will be returned if not filtered
#
# Examples:
# http://hostname:port/ion-service/list_resource_types
# http://hostname:port/ion-service/list_resource_types?type=InformationResource
# http://hostname:port/ion-service/list_resource_types?type=TaskableResource
#
@app.route('/ion-service/list_resource_types', methods=['GET','POST'])
def list_resource_types():

    try:
        #Look to see if a specific resource type has been specified - if not default to all
        if request.args.has_key('type'):
            resultSet = set(getextends(request.args['type'])) if getextends(request.args['type']) is not None else set()
        else:
            type_list = getextends('Resource')
            resultSet = set(type_list)

        ret_list = []
        for res in sorted(resultSet):
            ret_list.append(res)

        return jsonify(data=ret_list)

    except Exception, e:
        ret =  "Error: %s" % e.message
        return jsonify(data=ret)

#Returns a json object for a specified resource type with all default values.
@app.route('/ion-service/resource_type_schema/<resource_type>')
def get_resource_schema(resource_type):

    try:
        ion_object_name = convert_unicode(resource_type)
        ret_obj = IonObject(ion_object_name, {})

        # If it's an op input param or response message object.
        # Walk param list instantiating any params that were marked None as default.
        if hasattr(ret_obj, "_svc_name"):
            schema = ret_obj._schema
            for field in ret_obj._schema:
                if schema[field]["default"] is None:
                    try:
                        value = IonObject(schema[field]["type"], {})
                    except NotFound:
                        # TODO
                        # Some other non-IonObject type.  Just use None as default for now.
                        value = None
                    setattr(ret_obj, field, value)

        ret = simplejson.dumps(ret_obj, default=ion_object_encoder)

    except Exception, e:
        ret =  "Error: %s" % e.message

    return jsonify(data=ret)

#More RESTfull examples...should probably not use but here for example reference

#This example calls the resource registry with an id passed in as part of the URL
#http://hostname:port/ion-service/resource/c1b6fa6aadbd4eb696a9407a39adbdc8
@app.route('/ion-service/rest/resource/<resource_id>')
def get_resource(resource_id):

    ret = None
    client = ResourceRegistryServiceProcessClient(node=Container.instance.node, process=service_gateway_instance)
    if resource_id != '':
        try:
            result = client.read(resource_id)
            if not result:
                raise NotFound("No resource found for id: %s " % resource_id)

            ret = simplejson.dumps(result, default=ion_object_encoder)

        except Exception, e:
            ret =  "Error: %s" % e

    return jsonify(data=ret)

#Example operation to return a list of resources of a specific type like
#http://hostname:port/ion-service/find_resources/BankAccount
@app.route('/ion-service/rest/find_resources/<resource_type>')
def list_resources_by_type(resource_type):

    ret = None

    client = ResourceRegistryServiceProcessClient(node=Container.instance.node, process=service_gateway_instance)
    try:
        res_list,_ = client.find_resources(restype=resource_type )
        result = []
        for res in res_list:
            result.append(res)

        ret = simplejson.dumps(result, default=ion_object_encoder)
    except Exception, e:
        ret =  "Error: %s" % e

    return jsonify(data=ret)

#Example restful call to a client function for another service like
#http://hostname:port/ion-service/run_bank_client
@app.route('/ion-service/run_bank_client')
def create_accounts():
    from examples.bank.bank_client import run_client
    run_client(Container.instance, process=service_gateway_instance)
    return list_resources_by_type("BankAccount")

<|endoftext|>