<commit_before># Copyright (c) 2015 Uber Technologies, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import

import inspect
import logging
import os
import sys
from functools import partial

import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.tcpserver
from tornado.netutil import bind_sockets

from . import hyperbahn
from .. import scheme
from ..enum import enum
from ..event import EventEmitter
from ..event import EventRegistrar
from ..errors import AlreadyListeningError
from ..handler import CallableRequestHandler
from ..net import local_ip
from ..zipkin.zipkin_trace import ZipkinTraceHook
from .broker import ArgSchemeBroker
from .connection import StreamConnection
from .dispatch import RequestDispatcher
from .peer import PeerGroup

log = logging.getLogger('tchannel')

State = enum(
    'State',
    ready=0,
    closing=1,
    closed=2,
)

class TChannel(object):
    """Manages inbound and outbound connections to various hosts."""

    _SCHEMES = {
        'raw': scheme.RawArgScheme,
        'json': scheme.JsonArgScheme,
        # 'http': scheme.HttpArgScheme, TODO
    }

    def __init__(self, name, hostport=None, process_name=None,
                 known_peers=None, trace=False):
        """Build or re-use a TChannel.

        :param name:
            Name is used to identify client or service itself.

        :param hostport:
            The host-port at which the service behind this TChannel is
            reachable. The port specified in the ``hostport`` is what the
            server will listen on. If unspecified, the system will attempt to
            determine the local network IP for this host and use an
            OS-assigned port.

        :param process_name:
            Name of this process. This is used for logging only. If
            unspecified, this will default to ``$processName[$processId]``.

        :param known_peers:
            A list of host-ports at which already known peers can be reached.
            Defaults to an empty list.

        :param trace:
            Flag to turn on/off zipkin trace. It can be a bool variable or
            a function that return true or false.
        """
        self._state = State.ready
        self._handler = RequestDispatcher()

        self.peers = PeerGroup(self)

        self._port = 0
        self._host = None
        if hostport:
            self._host, port = hostport.rsplit(':', 1)
            self._port = int(port)

        if not self._host:
            # TChannel(":4040") => determine IP automatically but use port
            # 4040
            self._host = local_ip()

        self.process_name = process_name or "%s[%s]" % (
            sys.argv[0], os.getpid()
        )

        self.name = name
        self._trace = trace

        # register event hooks
        self.event_emitter = EventEmitter()
        self.hooks = EventRegistrar(self.event_emitter)
        self.hooks.register(ZipkinTraceHook(tchannel=self))

        if known_peers:
            for peer_hostport in known_peers:
                self.peers.add(peer_hostport)

        # server created from calling listening
        self._server = None

    @property
    def trace(self):
        if callable(self._trace):
            return self._trace()
        else:
            return self._trace

    def advertise(self, routers, name=None, timeout=None):
        """Make a service available on the Hyperbahn routing mesh.

        This will make contact with a Hyperbahn host from a list of known
        Hyperbahn routers. Additional Hyperbahn connections will be established
        once contact has been made with the network.

        :param router:
            A seed list of addresses of Hyperbahn routers, e.g.,
            ``["127.0.0.1:23000"]``.

        :param name:
            The identity of this service on the Hyperbahn.

            This is usually unnecessary, as it defaults to the name given when
            initializing the :py:class:`TChannel` (which is used as your
            identity as a caller).

        :returns:
            A future that resolves to the remote server's response after
            the first advertise finishes.

            Advertisement will continue to happen periodically.
        """
        name = name or self.name

        if not self.is_listening():
            self.listen()

        return hyperbahn.advertise(self, name, routers, timeout)

    @property
    def closed(self):
        return self._state == State.closed

    @tornado.gen.coroutine
    def close(self):
        if self._state in [State.closed, State.closing]:
            raise tornado.gen.Return(None)

        self._state = State.closing
        try:
            yield self.peers.clear()
        finally:
            self._state = State.closed

    @property
    def hostport(self):
        return "%s:%d" % (self._host, self._port)

    def request(self,
                hostport=None,
                service=None,
                arg_scheme=None,
                retry=None,
                **kwargs):
        """Initiate a new request through this TChannel.

        :param hostport:
            Host to which the request will be made. If unspecified, a random
            known peer will be picked. This is not necessary if using
            Hyperbahn.

        :param service:
            The name of a service available on Hyperbahn. Defaults to an empty
            string.

        :param arg_scheme:
            Determines the serialization scheme for the request. One of 'raw',
            'json', or 'thrift'. Defaults to 'raw'.

        :param rety:
            One of 'n' (never retry), 'c' (retry on connection errors), 't'
            (retry on timeout), 'ct' (retry on connection errors and timeouts).

            Defaults to 'c'.
        """
        return self.peers.request(hostport=hostport,
                                  service=service,
                                  arg_scheme=arg_scheme,
                                  retry=retry,
                                  **kwargs)

    def listen(self, port=None):
        """Start listening for incoming connections.

        A request handler must have already been specified with
        ``TChannel.host``.

        :param port:
            An explicit port to listen on. This is unnecessary when advertising
            on Hyperbahn.

        :returns:
            Returns immediately.

        :raises AlreadyListeningError:
            If listen was already called.
        """

        if self.is_listening():
            raise AlreadyListeningError(
                "listen has already been called"
            )

        if port:
            assert not self._port, "Port has already been set."
            self._port = int(port)

        assert self._handler, "Call .host with a RequestHandler first"
        server = TChannelServer(self)

        sockets = bind_sockets(self._port)
        assert sockets, "No sockets bound for port %d" % self._port

        # If port was 0, the OS probably assigned something better.
        self._port = sockets[0].getsockname()[1]

        server.add_sockets(sockets)

        # assign server so we don't listen twice
        self._server = server

    def is_listening(self):

        if self._server:
            return True

        return False

    @tornado.gen.coroutine
    def receive_call(self, message, connection):
        if not self._handler:
            log.warn(
                "Received %s but a handler has not been defined.", str(message)
            )
            return
        self._handler.handle(message, connection)

    def _register_simple(self, endpoint, scheme, f):
        """Register a simple endpoint with this TChannel.

        :param endpoint:
            Name of the endpoint being registered.
        :param scheme:
            Name of the arg scheme under which the endpoint will be
            registered.
        :param f:
            Callable handler for the endpoint.
        """
        assert scheme in self._SCHEMES, ("Unsupported arg scheme %s" % scheme)
        scheme = self._SCHEMES[scheme]()
        self._handler.register(endpoint, f, ArgSchemeBroker(scheme))
        return f

    def _register_thrift(self, service_module, handler, **kwargs):
        """Register a Thrift endpoint on this TChannel.

        :param service_module:
            Reference to the Thrift-generated module for the service being
            registered.
        :param handler:
            Handler for the endpoint
        :param method:
            Name of the Thrift method being registered. If omitted, ``f``'s
            name is assumed to be the method name.
        :param service:
            Name of the Thrift service. By default this is determined
            automatically from the module name.
        """
        import tchannel.thrift as thrift
        # Imported inside the function so that we don't have a hard dependency
        # on the Thrift library. This function is usable only if the Thrift
        # library is installed.
        thrift.register(self._handler, service_module, handler, **kwargs)
        return handler

    def register(self, endpoint, scheme=None, handler=None, **kwargs):
        """Register a handler with this TChannel.

        This may be used as a decorator:

        .. code-block:: python

            app = TChannel(name='foo')

            @app.register("hello", "json")
            def hello_handler(request, response, tchannel):
                params = yield request.get_body()

        Or as a function:

        .. code-block:: python

            # Here we have a Thrift handler for `Foo::hello`
            app.register(Foo, "hello", hello_thrift_handler)

        :param endpoint:
            Name of the endpoint being registered for raw and JSON arg
            schemes. Reference to the Thrift-generated module for the Thrift
            arg scheme.

        :param scheme:
            Name of the scheme under which the endpoint is being registered.
            One of "raw", "json", and "thrift". Defaults to "raw", except if
            "endpoint" was a module, in which case this defaults to "thrift".

        :param handler:
            If specified, this is the handler function. If ignored, this
            function returns a decorator that can be used to register the
            handler function.

        :returns:
            If ``handler`` was specified, this returns ``handler``. Otherwise,
            it returns a decorator that can be applied to a function to
            register it as the handler.
        """
        assert endpoint, "endpoint is required"

        if not scheme:
            # scheme defaults to raw, unless the endpoint is a service module.
            if inspect.ismodule(endpoint):
                scheme = "thrift"
            else:
                scheme = "raw"
        scheme = scheme.lower()

        if scheme == 'thrift':
            decorator = partial(self._register_thrift, endpoint, **kwargs)
        else:
            decorator = partial(
                self._register_simple, endpoint, scheme, **kwargs
            )

        if handler is not None:
            return decorator(handler)
        else:
            return decorator

class TChannelServer(tornado.tcpserver.TCPServer):
    __slots__ = ('tchannel',)

    def __init__(self, tchannel):
        super(TChannelServer, self).__init__()
        self.tchannel = tchannel

    @tornado.gen.coroutine
    def handle_stream(self, stream, address):
        log.debug("New incoming connection from %s:%d" % address)

        conn = StreamConnection(connection=stream, tchannel=self.tchannel)

        yield conn.expect_handshake(headers={
            'host_port': self.tchannel.hostport,
            'process_name': self.tchannel.process_name,
        })

        log.debug(
            "Successfully completed handshake with %s:%s (%s)",
            conn.remote_host,
            conn.remote_host_port,
            conn.remote_process_name)

        self.tchannel.peers.get(
            "%s:%s" % (conn.remote_host,
                       conn.remote_host_port)
        ).register_incoming(conn)

        yield conn.serve(handler=CallableRequestHandler(self._handle))

    def _handle(self, message, connection):
        self.tchannel.receive_call(message, connection)
<commit_msg>fix typo<commit_after># Copyright (c) 2015 Uber Technologies, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from __future__ import absolute_import

import inspect
import logging
import os
import sys
from functools import partial

import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.tcpserver
from tornado.netutil import bind_sockets

from . import hyperbahn
from .. import scheme
from ..enum import enum
from ..event import EventEmitter
from ..event import EventRegistrar
from ..errors import AlreadyListeningError
from ..handler import CallableRequestHandler
from ..net import local_ip
from ..zipkin.zipkin_trace import ZipkinTraceHook
from .broker import ArgSchemeBroker
from .connection import StreamConnection
from .dispatch import RequestDispatcher
from .peer import PeerGroup

log = logging.getLogger('tchannel')

State = enum(
    'State',
    ready=0,
    closing=1,
    closed=2,
)

class TChannel(object):
    """Manages inbound and outbound connections to various hosts."""

    _SCHEMES = {
        'raw': scheme.RawArgScheme,
        'json': scheme.JsonArgScheme,
        # 'http': scheme.HttpArgScheme, TODO
    }

    def __init__(self, name, hostport=None, process_name=None,
                 known_peers=None, trace=False):
        """Build or re-use a TChannel.

        :param name:
            Name is used to identify client or service itself.

        :param hostport:
            The host-port at which the service behind this TChannel is
            reachable. The port specified in the ``hostport`` is what the
            server will listen on. If unspecified, the system will attempt to
            determine the local network IP for this host and use an
            OS-assigned port.

        :param process_name:
            Name of this process. This is used for logging only. If
            unspecified, this will default to ``$processName[$processId]``.

        :param known_peers:
            A list of host-ports at which already known peers can be reached.
            Defaults to an empty list.

        :param trace:
            Flag to turn on/off zipkin trace. It can be a bool variable or
            a function that return true or false.
        """
        self._state = State.ready
        self._handler = RequestDispatcher()

        self.peers = PeerGroup(self)

        self._port = 0
        self._host = None
        if hostport:
            self._host, port = hostport.rsplit(':', 1)
            self._port = int(port)

        if not self._host:
            # TChannel(":4040") => determine IP automatically but use port
            # 4040
            self._host = local_ip()

        self.process_name = process_name or "%s[%s]" % (
            sys.argv[0], os.getpid()
        )

        self.name = name
        self._trace = trace

        # register event hooks
        self.event_emitter = EventEmitter()
        self.hooks = EventRegistrar(self.event_emitter)
        self.hooks.register(ZipkinTraceHook(tchannel=self))

        if known_peers:
            for peer_hostport in known_peers:
                self.peers.add(peer_hostport)

        # server created from calling listen()
        self._server = None

    @property
    def trace(self):
        if callable(self._trace):
            return self._trace()
        else:
            return self._trace

    def advertise(self, routers, name=None, timeout=None):
        """Make a service available on the Hyperbahn routing mesh.

        This will make contact with a Hyperbahn host from a list of known
        Hyperbahn routers. Additional Hyperbahn connections will be established
        once contact has been made with the network.

        :param router:
            A seed list of addresses of Hyperbahn routers, e.g.,
            ``["127.0.0.1:23000"]``.

        :param name:
            The identity of this service on the Hyperbahn.

            This is usually unnecessary, as it defaults to the name given when
            initializing the :py:class:`TChannel` (which is used as your
            identity as a caller).

        :returns:
            A future that resolves to the remote server's response after
            the first advertise finishes.

            Advertisement will continue to happen periodically.
        """
        name = name or self.name

        if not self.is_listening():
            self.listen()

        return hyperbahn.advertise(self, name, routers, timeout)

    @property
    def closed(self):
        return self._state == State.closed

    @tornado.gen.coroutine
    def close(self):
        if self._state in [State.closed, State.closing]:
            raise tornado.gen.Return(None)

        self._state = State.closing
        try:
            yield self.peers.clear()
        finally:
            self._state = State.closed

    @property
    def hostport(self):
        return "%s:%d" % (self._host, self._port)

    def request(self,
                hostport=None,
                service=None,
                arg_scheme=None,
                retry=None,
                **kwargs):
        """Initiate a new request through this TChannel.

        :param hostport:
            Host to which the request will be made. If unspecified, a random
            known peer will be picked. This is not necessary if using
            Hyperbahn.

        :param service:
            The name of a service available on Hyperbahn. Defaults to an empty
            string.

        :param arg_scheme:
            Determines the serialization scheme for the request. One of 'raw',
            'json', or 'thrift'. Defaults to 'raw'.

        :param rety:
            One of 'n' (never retry), 'c' (retry on connection errors), 't'
            (retry on timeout), 'ct' (retry on connection errors and timeouts).

            Defaults to 'c'.
        """
        return self.peers.request(hostport=hostport,
                                  service=service,
                                  arg_scheme=arg_scheme,
                                  retry=retry,
                                  **kwargs)

    def listen(self, port=None):
        """Start listening for incoming connections.

        A request handler must have already been specified with
        ``TChannel.host``.

        :param port:
            An explicit port to listen on. This is unnecessary when advertising
            on Hyperbahn.

        :returns:
            Returns immediately.

        :raises AlreadyListeningError:
            If listen was already called.
        """

        if self.is_listening():
            raise AlreadyListeningError(
                "listen has already been called"
            )

        if port:
            assert not self._port, "Port has already been set."
            self._port = int(port)

        assert self._handler, "Call .host with a RequestHandler first"
        server = TChannelServer(self)

        sockets = bind_sockets(self._port)
        assert sockets, "No sockets bound for port %d" % self._port

        # If port was 0, the OS probably assigned something better.
        self._port = sockets[0].getsockname()[1]

        server.add_sockets(sockets)

        # assign server so we don't listen twice
        self._server = server

    def is_listening(self):

        if self._server:
            return True

        return False

    @tornado.gen.coroutine
    def receive_call(self, message, connection):
        if not self._handler:
            log.warn(
                "Received %s but a handler has not been defined.", str(message)
            )
            return
        self._handler.handle(message, connection)

    def _register_simple(self, endpoint, scheme, f):
        """Register a simple endpoint with this TChannel.

        :param endpoint:
            Name of the endpoint being registered.
        :param scheme:
            Name of the arg scheme under which the endpoint will be
            registered.
        :param f:
            Callable handler for the endpoint.
        """
        assert scheme in self._SCHEMES, ("Unsupported arg scheme %s" % scheme)
        scheme = self._SCHEMES[scheme]()
        self._handler.register(endpoint, f, ArgSchemeBroker(scheme))
        return f

    def _register_thrift(self, service_module, handler, **kwargs):
        """Register a Thrift endpoint on this TChannel.

        :param service_module:
            Reference to the Thrift-generated module for the service being
            registered.
        :param handler:
            Handler for the endpoint
        :param method:
            Name of the Thrift method being registered. If omitted, ``f``'s
            name is assumed to be the method name.
        :param service:
            Name of the Thrift service. By default this is determined
            automatically from the module name.
        """
        import tchannel.thrift as thrift
        # Imported inside the function so that we don't have a hard dependency
        # on the Thrift library. This function is usable only if the Thrift
        # library is installed.
        thrift.register(self._handler, service_module, handler, **kwargs)
        return handler

    def register(self, endpoint, scheme=None, handler=None, **kwargs):
        """Register a handler with this TChannel.

        This may be used as a decorator:

        .. code-block:: python

            app = TChannel(name='foo')

            @app.register("hello", "json")
            def hello_handler(request, response, tchannel):
                params = yield request.get_body()

        Or as a function:

        .. code-block:: python

            # Here we have a Thrift handler for `Foo::hello`
            app.register(Foo, "hello", hello_thrift_handler)

        :param endpoint:
            Name of the endpoint being registered for raw and JSON arg
            schemes. Reference to the Thrift-generated module for the Thrift
            arg scheme.

        :param scheme:
            Name of the scheme under which the endpoint is being registered.
            One of "raw", "json", and "thrift". Defaults to "raw", except if
            "endpoint" was a module, in which case this defaults to "thrift".

        :param handler:
            If specified, this is the handler function. If ignored, this
            function returns a decorator that can be used to register the
            handler function.

        :returns:
            If ``handler`` was specified, this returns ``handler``. Otherwise,
            it returns a decorator that can be applied to a function to
            register it as the handler.
        """
        assert endpoint, "endpoint is required"

        if not scheme:
            # scheme defaults to raw, unless the endpoint is a service module.
            if inspect.ismodule(endpoint):
                scheme = "thrift"
            else:
                scheme = "raw"
        scheme = scheme.lower()

        if scheme == 'thrift':
            decorator = partial(self._register_thrift, endpoint, **kwargs)
        else:
            decorator = partial(
                self._register_simple, endpoint, scheme, **kwargs
            )

        if handler is not None:
            return decorator(handler)
        else:
            return decorator

class TChannelServer(tornado.tcpserver.TCPServer):
    __slots__ = ('tchannel',)

    def __init__(self, tchannel):
        super(TChannelServer, self).__init__()
        self.tchannel = tchannel

    @tornado.gen.coroutine
    def handle_stream(self, stream, address):
        log.debug("New incoming connection from %s:%d" % address)

        conn = StreamConnection(connection=stream, tchannel=self.tchannel)

        yield conn.expect_handshake(headers={
            'host_port': self.tchannel.hostport,
            'process_name': self.tchannel.process_name,
        })

        log.debug(
            "Successfully completed handshake with %s:%s (%s)",
            conn.remote_host,
            conn.remote_host_port,
            conn.remote_process_name)

        self.tchannel.peers.get(
            "%s:%s" % (conn.remote_host,
                       conn.remote_host_port)
        ).register_incoming(conn)

        yield conn.serve(handler=CallableRequestHandler(self._handle))

    def _handle(self, message, connection):
        self.tchannel.receive_call(message, connection)
<|endoftext|>


<commit_before>"""
sentry.plugins.bases.issue
~~~~~~~~~~~~~~~~~~~~~~~~~~

:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import

from django import forms
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from social_auth.models import UserSocialAuth

from sentry.models import GroupMeta, Activity
from sentry.plugins import Plugin
from sentry.utils.auth import get_auth_providers
from sentry.utils.http import absolute_uri

class NewIssueForm(forms.Form):
    title = forms.CharField(max_length=200, widget=forms.TextInput(attrs={'class': 'span9'}))
    description = forms.CharField(widget=forms.Textarea(attrs={'class': 'span9'}))

class IssuePlugin(Plugin):
    # project_conf_form = BaseIssueOptionsForm
    new_issue_form = NewIssueForm

    create_issue_template = 'sentry/plugins/bases/issue/create_issue.html'
    not_configured_template = 'sentry/plugins/bases/issue/not_configured.html'
    needs_auth_template = 'sentry/plugins/bases/issue/needs_auth.html'
    auth_provider = None

    def _get_group_body(self, request, group, event, **kwargs):
        interface = event.interfaces.get('sentry.interfaces.Stacktrace')
        if interface:
            return interface.to_string(event)
        return

    def _get_group_description(self, request, group, event):
        output = [
            absolute_uri(reverse('sentry-group', kwargs={
                'project_id': group.project.slug,
                'team_slug': group.organization.slug,
                'group_id': group.id,
            })),
        ]
        body = self._get_group_body(request, group, event)
        if body:
            output.extend([
                '',
                '```',
                body,
                '```',
            ])
        return '\n'.join(output)

    def _get_group_title(self, request, group, event):
        return event.error()

    def is_configured(self, request, project, **kwargs):
        raise NotImplementedError

    def get_auth_for_user(self, user, **kwargs):
        """
        Return a ``UserSocialAuth`` object for the given user based on this plugins ``auth_provider``.
        """
        assert self.auth_provider, 'There is no auth provider configured for this plugin.'

        if not user.is_authenticated():
            return None

        try:
            return UserSocialAuth.objects.filter(user=user, provider=self.auth_provider)[0]
        except IndexError:
            return None

    def needs_auth(self, request, project, **kwargs):
        """
        Return ``True`` if the authenticated user needs to associate an auth service before
        performing actions with this plugin.
        """
        if self.auth_provider is None:
            return False

        if not request.user.is_authenticated():
            return True

        return bool(not UserSocialAuth.objects.filter(user=request.user, provider=self.auth_provider).exists())

    def get_new_issue_title(self, **kwargs):
        """
        Return a string for the "Create new issue" action label.
        """
        return 'Create %s Issue' % self.get_title()

    def get_new_issue_form(self, request, group, event, **kwargs):
        """
        Return a Form for the "Create new issue" page.
        """
        return self.new_issue_form(request.POST or None, initial=self.get_initial_form_data(request, group, event))

    def get_issue_url(self, group, issue_id, **kwargs):
        """
        Given an issue_id (string) return an absolute URL to the issue's details
        page.
        """
        raise NotImplementedError

    def get_issue_label(self, group, issue_id, **kwargs):
        """
        Given an issue_id (string) return a string representing the issue.

        e.g. GitHub represents issues as GH-XXX
        """
        return '#%s' % issue_id

    def create_issue(self, request, group, form_data, **kwargs):
        """
        Creates the issue on the remote service and returns an issue ID.
        """
        raise NotImplementedError

    def get_initial_form_data(self, request, group, event, **kwargs):
        return {
            'description': self._get_group_description(request, group, event),
            'title': self._get_group_title(request, group, event),
        }

    def has_auth_configured(self, **kwargs):
        if not self.auth_provider:
            return True

        return self.auth_provider in get_auth_providers()

    def view(self, request, group, **kwargs):
        has_auth_configured = self.has_auth_configured()
        if not (has_auth_configured and self.is_configured(project=group.project, request=request)):
            if self.auth_provider:
                required_auth_settings = settings.AUTH_PROVIDERS[self.auth_provider]
            else:
                required_auth_settings = None

            return self.render(self.not_configured_template, {
                'title': self.get_title(),
                'project': group.project,
                'has_auth_configured': has_auth_configured,
                'required_auth_settings': required_auth_settings,
            })

        if self.needs_auth(project=group.project, request=request):
            return self.render(self.needs_auth_template, {
                'title': self.get_title(),
                'project': group.project,
            })

        if GroupMeta.objects.get_value(group, '%s:tid' % self.get_conf_key(), None):
            return None

        prefix = self.get_conf_key()
        event = group.get_latest_event()

        form = self.get_new_issue_form(request, group, event)
        if form.is_valid():
            try:
                issue_id = self.create_issue(
                    group=group,
                    form_data=form.cleaned_data,
                    request=request,
                )
            except forms.ValidationError as e:
                form.errors['__all__'] = [u'Error creating issue: %s' % e]

        if form.is_valid():
            GroupMeta.objects.set_value(group, '%s:tid' % prefix, issue_id)

            issue_information = {
                'title': form.cleaned_data['title'],
                'provider': self.get_title(),
                'location': self.get_issue_url(group, issue_id),
            }
            Activity.objects.create(
                project=group.project,
                group=group,
                type=Activity.CREATE_ISSUE,
                user=request.user,
                data=issue_information,
            )

            return self.redirect(reverse('sentry-group', args=[group.organization.slug, group.project_id, group.pk]))

        context = {
            'form': form,
            'title': self.get_new_issue_title(),
        }

        return self.render(self.create_issue_template, context)

    def actions(self, request, group, action_list, **kwargs):
        if not self.is_configured(request=request, project=group.project):
            return action_list
        prefix = self.get_conf_key()
        if not GroupMeta.objects.get_value(group, '%s:tid' % prefix, None):
            action_list.append((self.get_new_issue_title(), self.get_url(group)))
        return action_list

    def tags(self, request, group, tag_list, **kwargs):
        if not self.is_configured(request=request, project=group.project):
            return tag_list

        prefix = self.get_conf_key()
        issue_id = GroupMeta.objects.get_value(group, '%s:tid' % prefix)
        if not issue_id:
            return tag_list

        tag_list.append(mark_safe('%s' % (
            self.get_issue_url(group=group, issue_id=issue_id),
            escape(self.get_issue_label(group=group, issue_id=issue_id)),
        )))

        return tag_list
<commit_msg>Correct url<commit_after>"""
sentry.plugins.bases.issue
~~~~~~~~~~~~~~~~~~~~~~~~~~

:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import

from django import forms
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from social_auth.models import UserSocialAuth

from sentry.models import GroupMeta, Activity
from sentry.plugins import Plugin
from sentry.utils.auth import get_auth_providers
from sentry.utils.http import absolute_uri

class NewIssueForm(forms.Form):
    title = forms.CharField(max_length=200, widget=forms.TextInput(attrs={'class': 'span9'}))
    description = forms.CharField(widget=forms.Textarea(attrs={'class': 'span9'}))

class IssuePlugin(Plugin):
    # project_conf_form = BaseIssueOptionsForm
    new_issue_form = NewIssueForm

    create_issue_template = 'sentry/plugins/bases/issue/create_issue.html'
    not_configured_template = 'sentry/plugins/bases/issue/not_configured.html'
    needs_auth_template = 'sentry/plugins/bases/issue/needs_auth.html'
    auth_provider = None

    def _get_group_body(self, request, group, event, **kwargs):
        interface = event.interfaces.get('sentry.interfaces.Stacktrace')
        if interface:
            return interface.to_string(event)
        return

    def _get_group_description(self, request, group, event):
        output = [
            absolute_uri(reverse('sentry-group', kwargs={
                'project_id': group.project.slug,
                'organization_slug': group.organization.slug,
                'group_id': group.id,
            })),
        ]
        body = self._get_group_body(request, group, event)
        if body:
            output.extend([
                '',
                '```',
                body,
                '```',
            ])
        return '\n'.join(output)

    def _get_group_title(self, request, group, event):
        return event.error()

    def is_configured(self, request, project, **kwargs):
        raise NotImplementedError

    def get_auth_for_user(self, user, **kwargs):
        """
        Return a ``UserSocialAuth`` object for the given user based on this plugins ``auth_provider``.
        """
        assert self.auth_provider, 'There is no auth provider configured for this plugin.'

        if not user.is_authenticated():
            return None

        try:
            return UserSocialAuth.objects.filter(user=user, provider=self.auth_provider)[0]
        except IndexError:
            return None

    def needs_auth(self, request, project, **kwargs):
        """
        Return ``True`` if the authenticated user needs to associate an auth service before
        performing actions with this plugin.
        """
        if self.auth_provider is None:
            return False

        if not request.user.is_authenticated():
            return True

        return bool(not UserSocialAuth.objects.filter(user=request.user, provider=self.auth_provider).exists())

    def get_new_issue_title(self, **kwargs):
        """
        Return a string for the "Create new issue" action label.
        """
        return 'Create %s Issue' % self.get_title()

    def get_new_issue_form(self, request, group, event, **kwargs):
        """
        Return a Form for the "Create new issue" page.
        """
        return self.new_issue_form(request.POST or None, initial=self.get_initial_form_data(request, group, event))

    def get_issue_url(self, group, issue_id, **kwargs):
        """
        Given an issue_id (string) return an absolute URL to the issue's details
        page.
        """
        raise NotImplementedError

    def get_issue_label(self, group, issue_id, **kwargs):
        """
        Given an issue_id (string) return a string representing the issue.

        e.g. GitHub represents issues as GH-XXX
        """
        return '#%s' % issue_id

    def create_issue(self, request, group, form_data, **kwargs):
        """
        Creates the issue on the remote service and returns an issue ID.
        """
        raise NotImplementedError

    def get_initial_form_data(self, request, group, event, **kwargs):
        return {
            'description': self._get_group_description(request, group, event),
            'title': self._get_group_title(request, group, event),
        }

    def has_auth_configured(self, **kwargs):
        if not self.auth_provider:
            return True

        return self.auth_provider in get_auth_providers()

    def view(self, request, group, **kwargs):
        has_auth_configured = self.has_auth_configured()
        if not (has_auth_configured and self.is_configured(project=group.project, request=request)):
            if self.auth_provider:
                required_auth_settings = settings.AUTH_PROVIDERS[self.auth_provider]
            else:
                required_auth_settings = None

            return self.render(self.not_configured_template, {
                'title': self.get_title(),
                'project': group.project,
                'has_auth_configured': has_auth_configured,
                'required_auth_settings': required_auth_settings,
            })

        if self.needs_auth(project=group.project, request=request):
            return self.render(self.needs_auth_template, {
                'title': self.get_title(),
                'project': group.project,
            })

        if GroupMeta.objects.get_value(group, '%s:tid' % self.get_conf_key(), None):
            return None

        prefix = self.get_conf_key()
        event = group.get_latest_event()

        form = self.get_new_issue_form(request, group, event)
        if form.is_valid():
            try:
                issue_id = self.create_issue(
                    group=group,
                    form_data=form.cleaned_data,
                    request=request,
                )
            except forms.ValidationError as e:
                form.errors['__all__'] = [u'Error creating issue: %s' % e]

        if form.is_valid():
            GroupMeta.objects.set_value(group, '%s:tid' % prefix, issue_id)

            issue_information = {
                'title': form.cleaned_data['title'],
                'provider': self.get_title(),
                'location': self.get_issue_url(group, issue_id),
            }
            Activity.objects.create(
                project=group.project,
                group=group,
                type=Activity.CREATE_ISSUE,
                user=request.user,
                data=issue_information,
            )

            return self.redirect(reverse('sentry-group', args=[group.organization.slug, group.project_id, group.pk]))

        context = {
            'form': form,
            'title': self.get_new_issue_title(),
        }

        return self.render(self.create_issue_template, context)

    def actions(self, request, group, action_list, **kwargs):
        if not self.is_configured(request=request, project=group.project):
            return action_list
        prefix = self.get_conf_key()
        if not GroupMeta.objects.get_value(group, '%s:tid' % prefix, None):
            action_list.append((self.get_new_issue_title(), self.get_url(group)))
        return action_list

    def tags(self, request, group, tag_list, **kwargs):
        if not self.is_configured(request=request, project=group.project):
            return tag_list

        prefix = self.get_conf_key()
        issue_id = GroupMeta.objects.get_value(group, '%s:tid' % prefix)
        if not issue_id:
            return tag_list

        tag_list.append(mark_safe('%s' % (
            self.get_issue_url(group=group, issue_id=issue_id),
            escape(self.get_issue_label(group=group, issue_id=issue_id)),
        )))

        return tag_list
<|endoftext|>


<commit_before># -*- coding: utf-8; mode: Python -*-

"""OCitySMap.

Provide documentation here.
"""

__author__ = 'The Hackfest2009 team'
__version__ = '0.1'

import logging
import pgdb
import math
import re

l = logging.getLogger('ocitysmap')

APPELLATIONS = [ "Allée", "Avenue", "Boulevard", "Carrefour", "Chaussée",
                 "Chemin", "Cité", "Clos", "Côte", "Cour", "Cours", "Degré",
                 "Esplanade", "Impasse", "Liaison", "Mail", "Montée",
                 "Passage", "Place", "Placette", "Pont", "Promenade", "Quai",
                 "Résidence", "Rond-Point", "Rang", "Route", "Rue", "Ruelle",
                 "Square", "Traboule", "Traverse", "Venelle", "Voie", "Rond-point" ]

DETERMINANTS = [" des", " du", " de la", " de l'", " de", " d'", ""]

SPACE_REDUCE = re.compile(r"\s+")
PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.*)" %
                           ("|".join(APPELLATIONS),
                            "|".join(DETERMINANTS)))

class BaseOCitySMapError(Exception):
    """Base class for exceptions thrown by OCitySMap."""

class UnsufficientDataError(BaseOCitySMapError):
    """Not enough data in the OSM database to proceed."""

class BoundingBox:
    def __init__(self, lat1, long1, lat2, long2):
        (self._lat1, self._long1) = float(lat1), float(long1)
        (self._lat2, self._long2) = float(lat2), float(long2)

        # Validate bounding box?

    @staticmethod
    def parse(points):
        (lat1, long1) = points[0].split(',')
        (lat2, long2) = points[1].split(',')
        return BoundingBox(lat1, long1, lat2, long2)

    def get_top_left(self):
        return (self._lat1, self._long1)

    def get_bottom_right(self):
        return (self._lat2, self._long2)

    def ptstr(self, point):
        return '%.4f,%.4f' % (point[0], point[1])

    def __str__(self):
        return '(%s %s)' % (self.ptstr(self.get_top_left()),
                            self.ptstr(self.get_bottom_right()))

def _gen_vertical_square_label(x):
    label = ''
    while x != -1:
        label = chr(ord('A') + x % 26) + label
        x /= 26
        x -= 1
    return label

def _gen_horizontal_square_label(x):
    return str(x + 1)

class MapDescriptor:
    def __init__(self, bbox, db):
        self.bbox = bbox
        cursor = db.cursor()

        # Compute width and heights in meters of the bounding box
        cursor.execute("""select
                          st_distance_sphere(st_geomfromtext('POINT(%f %f)', 4002),
                                             st_geomfromtext('POINT(%f %f)', 4002))""" % \
                           (bbox.get_top_left()[0], bbox.get_top_left()[1],
                            bbox.get_top_left()[0], bbox.get_bottom_right()[1]))
        width = cursor.fetchall()[0][0]
        cursor.execute("""select
                          st_distance_sphere(st_geomfromtext('POINT(%f %f)', 4002),
                                             st_geomfromtext('POINT(%f %f)', 4002))""" % \
                           (bbox.get_top_left()[0], bbox.get_top_left()[1],
                            bbox.get_bottom_right()[0], bbox.get_top_left()[1]))
        height = cursor.fetchall()[0][0]

        # Compute number of squares, assumming a size of 500 meters
        # per square
        self.width_square_count  = width / 500
        self.height_square_count = height / 500

        # Compute the size in angles of the squares
        self.width_square_angle  = (abs(bbox.get_top_left()[1] - bbox.get_bottom_right()[1]) /
                                    self.width_square_count)
        self.height_square_angle = (abs(bbox.get_top_left()[0] - bbox.get_bottom_right()[0]) /
                                    self.height_square_count)

        # Compute the lists of longitudes and latitudes of the
        # horizontal and vertical lines delimiting the square
        self.vertical_lines   = [bbox.get_top_left()[1] + x * self.width_square_angle
                                 for x in xrange(0, int(math.ceil(self.width_square_count )) + 1)]
        self.horizontal_lines = [bbox.get_top_left()[0] - x * self.height_square_angle
                                 for x in xrange(0, int(math.ceil(self.height_square_count)) + 1)]

        # Compute the lists of labels
        self.vertical_labels   = [_gen_vertical_square_label(x)
                                  for x in xrange(0, int(math.ceil(self.width_square_count)))]
        self.horizontal_labels = [_gen_horizontal_square_label(x)
                                  for x in xrange(0, int(math.ceil(self.height_square_count)))]
        l.debug("vertical lines: %s" % self.vertical_lines)
        l.debug("horizontal lines: %s" % self.horizontal_lines)
        l.debug("vertical labels: %s" % self.vertical_labels)
        l.debug("horizontal labels: %s" % self.horizontal_labels)

def _humanize_street_label(street):

    def unprefix_street(name):
        name = name.strip()
        name = SPACE_REDUCE.sub(" ", name)
        return PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)

    def couple_compare(x,y):
        a = y[0] - x[0]
        if a:
            return a
        return y[1] - x[1]

    def distance(a,b):
        return (b[0]-a[0])**2 + (b[1]-a[1])**2

    name = unprefix_street(street[0])
    squares = street[1]
    minx = min([x[0] for x in squares])
    maxx = max([x[0] for x in squares])
    miny = min([x[1] for x in squares])
    maxy = max([x[1] for x in squares])
    if len(squares) == 1:
        label = (_gen_vertical_square_label(squares[0][0]) +
                 _gen_horizontal_square_label(squares[0][1]))
    elif minx == maxx:
        label = ('%s%s-%s' % (_gen_vertical_square_label(minx),
                              _gen_horizontal_square_label(miny),
                              _gen_horizontal_square_label(maxy)))
    elif miny == maxy:
        label = ('%s-%s%s' % (_gen_vertical_square_label(minx),
                              _gen_vertical_square_label(maxx),
                              _gen_horizontal_square_label(miny)))
    elif (maxx - minx + 1) * (maxy - miny + 1) == len(squares):
        label = ('%s-%s%s-%s' % (_gen_vertical_square_label(minx),
                                 _gen_vertical_square_label(maxx),
                                 _gen_horizontal_square_label(miny),
                                 _gen_horizontal_square_label(maxy)))
    else:
        squares_x_first = sorted(squares, couple_compare)
        squares_y_first = sorted(squares, lambda x,y: couple_compare(y,x))
        if (distance(squares_x_first[0], squares_x_first[-1]) >
            distance(squares_y_first[0], squares_y_first[-1])):
            first = squares_x_first[0]
            last = squares_x_first[-1]
        else:
            first = squares_y_first[0]
            last = squares_y_first[-1]

        label = '%s%s...%s%s' % (_gen_vertical_square_label(first[0]),
                                 _gen_horizontal_square_label(first[1]),
                                 _gen_vertical_square_label(last[0]),
                                 _gen_horizontal_square_label(last[1]))
    return (name, label)

class OCitySMap:
    def __init__(self, name, boundingbox=None, zooms=[]):
        """Creates a new OCitySMap renderer instance for the given city.

        Args:
            name (string): The name of the city we're created the map of.
            boundingbox (BoundingBox): An optional BoundingBox object defining
                the city's bounding box. If not given, OCitySMap will try to
                guess the bounding box from the OSM data. An UnsufficientDataError
                exception will be raised in the bounding box can't be guessed.
            zooms (dict): A dictionnary of zoom sections to add to the map. The
                dictionnary maps a zoom box title to its bounding box
                (BoundingBox objects).
        """
        (self.name, self.boundingbox, self.zooms) = (name, boundingbox, zooms)

        l.info('OCitySMap renderer for %s.' % self.name)
        l.info('%d zoom section(s).' % len(self.zooms))
        for name, box in self.zooms.iteritems():
            l.debug('"%s": %s' % (name, str(box)))

        if not self.boundingbox:
            self.boundingbox = self.find_bounding_box(self.name)

        db = pgdb.connect('Notre Base', 'test', 'test', 'surf.local', 'testdb')
        self.mapdesc = MapDescriptor(self.boundingbox, db)

        self.streets = self.get_streets(db)
        l.debug('Streets: %s' % self.streets)

        l.info('City bounding box is %s.' % str(self.boundingbox))

    def find_bounding_box(self, name):
        """Find the bounding box of a city from its name.

        Args:
            name (string): The city name.
        Returns a 4-uple of GPS coordinates describing the bounding box around
        the given city (top-left, top-right, bottom-right, bottom-left).
        """

        l.info('Looking for bounding box around %s...' % name)
        raise UnsufficientDataError, "Not enough data to find city bounding box!"

    def get_streets(self, db):

        """Get the list of streets in the bounding box, and for each
        street, the list of squares that it intersects.

        Returns a list of the form [(street_name, [[0, 1], [1, 1]]),
                                    (street2_name, [[1, 2]])]
        """
        cursor = db.cursor()
        cursor.execute("drop table if exists map_areas")
        cursor.execute("create table map_areas (x integer, y integer)")
        cursor.execute("select addgeometrycolumn('map_areas', 'geom', 4002, 'POLYGON', 2)")
        for i in xrange(0, int(math.ceil(self.mapdesc.width_square_count))):
            for j in xrange(0, int(math.ceil(self.mapdesc.height_square_count))):
                lon1 = self.boundingbox.get_top_left()[1] + i * self.mapdesc.width_square_angle
                lon2 = self.boundingbox.get_top_left()[1] + (i + 1) * self.mapdesc.width_square_angle
                lat1 = self.boundingbox.get_top_left()[0] - j * self.mapdesc.height_square_angle
                lat2 = self.boundingbox.get_top_left()[0] - (j + 1) * self.mapdesc.height_square_angle
                poly = "POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))" % \
                    (lon1, lat1, lon1, lat2, lon2, lat2, lon2, lat1, lon1, lat1)
                cursor.execute("""insert into map_areas (x, y, geom)
                                         values (%d, %d, st_geomfromtext('%s', 4002))""" % \
                                   (i, j, poly))
        db.commit()
        cursor.execute("""select name, textcat_all(x || ',' || y || ';')
                          from (select distinct name, x, y
                                from planet_osm_line
                                join map_areas
                                on st_intersects(way, st_transform(geom, 900913))
                                where name != '' and highway is not null)
                          as foo
                          group by name
                          order by name;""")
        sl = cursor.fetchall()
        sl = [(street[0], [map(int, x.split(',')) for x in street[1].split(';')[:-1]]) for street in sl]
        return sorted(map(_humanize_street_label, sl), lambda x, y: cmp(x[0], y[0]))

<commit_msg>Case insensitive prefix matching and street sorting<commit_after># -*- coding: utf-8; mode: Python -*-

"""OCitySMap.

Provide documentation here.
"""

__author__ = 'The Hackfest2009 team'
__version__ = '0.1'

import logging
import pgdb
import math
import re

l = logging.getLogger('ocitysmap')

APPELLATIONS = [ "Allée", "Avenue", "Boulevard", "Carrefour", "Chaussée",
                 "Chemin", "Cité", "Clos", "Côte", "Cour", "Cours", "Degré",
                 "Esplanade", "Impasse", "Liaison", "Mail", "Montée",
                 "Passage", "Place", "Placette", "Pont", "Promenade", "Quai",
                 "Résidence", "Rond-Point", "Rang", "Route", "Rue", "Ruelle",
                 "Square", "Traboule", "Traverse", "Venelle", "Voie", "Rond-point" ]

DETERMINANTS = [" des", " du", " de la", " de l'", " de", " d'", ""]

SPACE_REDUCE = re.compile(r"\s+")
PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.*)" %
                           ("|".join(APPELLATIONS),
                            "|".join(DETERMINANTS)), re.IGNORECASE)

class BaseOCitySMapError(Exception):
    """Base class for exceptions thrown by OCitySMap."""

class UnsufficientDataError(BaseOCitySMapError):
    """Not enough data in the OSM database to proceed."""

class BoundingBox:
    def __init__(self, lat1, long1, lat2, long2):
        (self._lat1, self._long1) = float(lat1), float(long1)
        (self._lat2, self._long2) = float(lat2), float(long2)

        # Validate bounding box?

    @staticmethod
    def parse(points):
        (lat1, long1) = points[0].split(',')
        (lat2, long2) = points[1].split(',')
        return BoundingBox(lat1, long1, lat2, long2)

    def get_top_left(self):
        return (self._lat1, self._long1)

    def get_bottom_right(self):
        return (self._lat2, self._long2)

    def ptstr(self, point):
        return '%.4f,%.4f' % (point[0], point[1])

    def __str__(self):
        return '(%s %s)' % (self.ptstr(self.get_top_left()),
                            self.ptstr(self.get_bottom_right()))

def _gen_vertical_square_label(x):
    label = ''
    while x != -1:
        label = chr(ord('A') + x % 26) + label
        x /= 26
        x -= 1
    return label

def _gen_horizontal_square_label(x):
    return str(x + 1)

class MapDescriptor:
    def __init__(self, bbox, db):
        self.bbox = bbox
        cursor = db.cursor()

        # Compute width and heights in meters of the bounding box
        cursor.execute("""select
                          st_distance_sphere(st_geomfromtext('POINT(%f %f)', 4002),
                                             st_geomfromtext('POINT(%f %f)', 4002))""" % \
                           (bbox.get_top_left()[0], bbox.get_top_left()[1],
                            bbox.get_top_left()[0], bbox.get_bottom_right()[1]))
        width = cursor.fetchall()[0][0]
        cursor.execute("""select
                          st_distance_sphere(st_geomfromtext('POINT(%f %f)', 4002),
                                             st_geomfromtext('POINT(%f %f)', 4002))""" % \
                           (bbox.get_top_left()[0], bbox.get_top_left()[1],
                            bbox.get_bottom_right()[0], bbox.get_top_left()[1]))
        height = cursor.fetchall()[0][0]

        # Compute number of squares, assumming a size of 500 meters
        # per square
        self.width_square_count  = width / 500
        self.height_square_count = height / 500

        # Compute the size in angles of the squares
        self.width_square_angle  = (abs(bbox.get_top_left()[1] - bbox.get_bottom_right()[1]) /
                                    self.width_square_count)
        self.height_square_angle = (abs(bbox.get_top_left()[0] - bbox.get_bottom_right()[0]) /
                                    self.height_square_count)

        # Compute the lists of longitudes and latitudes of the
        # horizontal and vertical lines delimiting the square
        self.vertical_lines   = [bbox.get_top_left()[1] + x * self.width_square_angle
                                 for x in xrange(0, int(math.ceil(self.width_square_count )) + 1)]
        self.horizontal_lines = [bbox.get_top_left()[0] - x * self.height_square_angle
                                 for x in xrange(0, int(math.ceil(self.height_square_count)) + 1)]

        # Compute the lists of labels
        self.vertical_labels   = [_gen_vertical_square_label(x)
                                  for x in xrange(0, int(math.ceil(self.width_square_count)))]
        self.horizontal_labels = [_gen_horizontal_square_label(x)
                                  for x in xrange(0, int(math.ceil(self.height_square_count)))]
        l.debug("vertical lines: %s" % self.vertical_lines)
        l.debug("horizontal lines: %s" % self.horizontal_lines)
        l.debug("vertical labels: %s" % self.vertical_labels)
        l.debug("horizontal labels: %s" % self.horizontal_labels)

def _humanize_street_label(street):

    def unprefix_street(name):
        name = name.strip()
        name = SPACE_REDUCE.sub(" ", name)
        return PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)

    def couple_compare(x,y):
        a = y[0] - x[0]
        if a:
            return a
        return y[1] - x[1]

    def distance(a,b):
        return (b[0]-a[0])**2 + (b[1]-a[1])**2

    name = unprefix_street(street[0])
    squares = street[1]
    minx = min([x[0] for x in squares])
    maxx = max([x[0] for x in squares])
    miny = min([x[1] for x in squares])
    maxy = max([x[1] for x in squares])
    if len(squares) == 1:
        label = (_gen_vertical_square_label(squares[0][0]) +
                 _gen_horizontal_square_label(squares[0][1]))
    elif minx == maxx:
        label = ('%s%s-%s' % (_gen_vertical_square_label(minx),
                              _gen_horizontal_square_label(miny),
                              _gen_horizontal_square_label(maxy)))
    elif miny == maxy:
        label = ('%s-%s%s' % (_gen_vertical_square_label(minx),
                              _gen_vertical_square_label(maxx),
                              _gen_horizontal_square_label(miny)))
    elif (maxx - minx + 1) * (maxy - miny + 1) == len(squares):
        label = ('%s-%s%s-%s' % (_gen_vertical_square_label(minx),
                                 _gen_vertical_square_label(maxx),
                                 _gen_horizontal_square_label(miny),
                                 _gen_horizontal_square_label(maxy)))
    else:
        squares_x_first = sorted(squares, couple_compare)
        squares_y_first = sorted(squares, lambda x,y: couple_compare(y,x))
        if (distance(squares_x_first[0], squares_x_first[-1]) >
            distance(squares_y_first[0], squares_y_first[-1])):
            first = squares_x_first[0]
            last = squares_x_first[-1]
        else:
            first = squares_y_first[0]
            last = squares_y_first[-1]

        label = '%s%s...%s%s' % (_gen_vertical_square_label(first[0]),
                                 _gen_horizontal_square_label(first[1]),
                                 _gen_vertical_square_label(last[0]),
                                 _gen_horizontal_square_label(last[1]))
    return (name, label)

class OCitySMap:
    def __init__(self, name, boundingbox=None, zooms=[]):
        """Creates a new OCitySMap renderer instance for the given city.

        Args:
            name (string): The name of the city we're created the map of.
            boundingbox (BoundingBox): An optional BoundingBox object defining
                the city's bounding box. If not given, OCitySMap will try to
                guess the bounding box from the OSM data. An UnsufficientDataError
                exception will be raised in the bounding box can't be guessed.
            zooms (dict): A dictionnary of zoom sections to add to the map. The
                dictionnary maps a zoom box title to its bounding box
                (BoundingBox objects).
        """
        (self.name, self.boundingbox, self.zooms) = (name, boundingbox, zooms)

        l.info('OCitySMap renderer for %s.' % self.name)
        l.info('%d zoom section(s).' % len(self.zooms))
        for name, box in self.zooms.iteritems():
            l.debug('"%s": %s' % (name, str(box)))

        if not self.boundingbox:
            self.boundingbox = self.find_bounding_box(self.name)

        db = pgdb.connect('Notre Base', 'test', 'test', 'surf.local', 'testdb')
        self.mapdesc = MapDescriptor(self.boundingbox, db)

        self.streets = self.get_streets(db)
        l.debug('Streets: %s' % self.streets)

        l.info('City bounding box is %s.' % str(self.boundingbox))

    def find_bounding_box(self, name):
        """Find the bounding box of a city from its name.

        Args:
            name (string): The city name.
        Returns a 4-uple of GPS coordinates describing the bounding box around
        the given city (top-left, top-right, bottom-right, bottom-left).
        """

        l.info('Looking for bounding box around %s...' % name)
        raise UnsufficientDataError, "Not enough data to find city bounding box!"

    def get_streets(self, db):

        """Get the list of streets in the bounding box, and for each
        street, the list of squares that it intersects.

        Returns a list of the form [(street_name, [[0, 1], [1, 1]]),
                                    (street2_name, [[1, 2]])]
        """
        cursor = db.cursor()
        cursor.execute("drop table if exists map_areas")
        cursor.execute("create table map_areas (x integer, y integer)")
        cursor.execute("select addgeometrycolumn('map_areas', 'geom', 4002, 'POLYGON', 2)")
        for i in xrange(0, int(math.ceil(self.mapdesc.width_square_count))):
            for j in xrange(0, int(math.ceil(self.mapdesc.height_square_count))):
                lon1 = self.boundingbox.get_top_left()[1] + i * self.mapdesc.width_square_angle
                lon2 = self.boundingbox.get_top_left()[1] + (i + 1) * self.mapdesc.width_square_angle
                lat1 = self.boundingbox.get_top_left()[0] - j * self.mapdesc.height_square_angle
                lat2 = self.boundingbox.get_top_left()[0] - (j + 1) * self.mapdesc.height_square_angle
                poly = "POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))" % \
                    (lon1, lat1, lon1, lat2, lon2, lat2, lon2, lat1, lon1, lat1)
                cursor.execute("""insert into map_areas (x, y, geom)
                                         values (%d, %d, st_geomfromtext('%s', 4002))""" % \
                                   (i, j, poly))
        db.commit()
        cursor.execute("""select name, textcat_all(x || ',' || y || ';')
                          from (select distinct name, x, y
                                from planet_osm_line
                                join map_areas
                                on st_intersects(way, st_transform(geom, 900913))
                                where name != '' and highway is not null)
                          as foo
                          group by name
                          order by name;""")
        sl = cursor.fetchall()
        sl = [(street[0], [map(int, x.split(',')) for x in street[1].split(';')[:-1]]) for street in sl]
        return sorted(map(_humanize_street_label, sl), lambda x, y: cmp(x[0].lower(), y[0].lower()))

<|endoftext|>


<commit_before>#!/usr/bin/env python
#-*- encoding: utf8 -*-

import os
import yaml
import json
import rospy
from mhri_msgs.msg import ForwardingEvent
from mhri_msgs.srv import WriteData, WriteDataRequest, ReadData, RegisterData, RegisterDataRequest

class PerceptionBase(object):
    def __init__(self, node_name):
        rospy.init_node(node_name, anonymous=False)

        try:
            conf_file = rospy.get_param('~config_file')
        except KeyError as e:
            rospy.logerr('You should set the parameter for perception config file...')
            quit()

        with open(os.path.abspath(conf_file)) as f:
            self.conf_data = yaml.load(f.read())
            rospy.loginfo('loading perception config file... %d perception(s) exists...'%len(self.conf_data.keys()))
            for item in self.conf_data.keys():
                rospy.loginfo('\033[92m  - %s: %d event(s) and %d data(s).\033[0m'%(item, len(self.conf_data[item]['events']), len(self.conf_data[item]['data'])))

        self.dict_srv_wr = {}
        self.dict_srv_rd = {}

        for item in self.conf_data.keys():
            if self.conf_data[item].has_key('target_memory'):
                memory_name = self.conf_data[item]['target_memory']
                rospy.loginfo('\033[94m[%s]\033[0m wait for bringup %s...'%(rospy.get_name(), memory_name))

                rospy.wait_for_service('/%s/write_data'%memory_name)
                self.dict_srv_wr[memory_name] = rospy.ServiceProxy('/%s/write_data'%memory_name, WriteData)
                self.dict_srv_rd[memory_name] = rospy.ServiceProxy('/%s/read_data'%memory_name, ReadData)

                self.register_data_to_memory(memory_name, item, self.conf_data[item]['data'])

        self.pub_event = rospy.Publisher('forwarding_event', ForwardingEvent, queue_size=10)
        rospy.loginfo('\033[94m[%s]\033[0m initialize perception_base done...'%rospy.get_name())

    def raise_event(self, perception_item, event):
        if not perception_item in self.conf_data.keys():
            rospy.logwarn('<%s> perception is not member of perception configuration...'%perception_item)
            return
        if not event in self.conf_data[perception_item]['events']:
            rospy.logwarn('<%s> event is not member of event list of perception configuration...'%event)
            return

        msg = ForwardingEvent()
        msg.header.stamp = rospy.Time.now()
        msg.event = event
        msg.by = perception_item

        self.pub_event.publish(msg)

    def register_data_to_memory(self, memory_name, perception_name, data):
        rospy.wait_for_service('/%s/register_data'%memory_name)
        srv_register = rospy.ServiceProxy('/%s/register_data'%memory_name, RegisterData)

        srv_req = RegisterDataRequest()
        srv_req.perception_name = perception_name
        srv_req.data = json.dumps(data)

        return srv_register(srv_req)

    def save_to_memory(self, perception_name, data={}):
        if data == {}:
            rospy.logwarn('Empty data inserted...')
            return

        for item in data.keys():
            if not item in self.conf_data[perception_name]['data'].keys():
                rospy.logwarn('Wrong data inserted...')
                return

        srv_req = WriteDataRequest()
        srv_req.perception_name = perception_name
        srv_req.data = json.dumps(data)
        srv_req.by = rospy.get_name()

        target_memory = self.conf_data[perception_name]['target_memory']
        self.dict_srv_wr[target_memory](srv_req)

    def read_from_memory(self, target_memory, data):
        pass
<commit_msg>Add function start/stop for social_perception<commit_after>#!/usr/bin/env python
#-*- encoding: utf8 -*-

import os
import yaml
import json
import rospy
from mhri_msgs.msg import ForwardingEvent
from mhri_msgs.srv import WriteData, WriteDataRequest, ReadData, RegisterData, RegisterDataRequest
from std_srvs.srv import Empty

class PerceptionBase(object):
    def __init__(self, node_name):
        rospy.init_node(node_name, anonymous=False)

        try:
            conf_file = rospy.get_param('~config_file')
        except KeyError as e:
            rospy.logerr('You should set the parameter for perception config file...')
            quit()

        with open(os.path.abspath(conf_file)) as f:
            self.conf_data = yaml.load(f.read())
            rospy.loginfo('loading perception config file... %d perception(s) exists...'%len(self.conf_data.keys()))
            for item in self.conf_data.keys():
                rospy.loginfo('\033[92m  - %s: %d event(s) and %d data(s).\033[0m'%(item, len(self.conf_data[item]['events']), len(self.conf_data[item]['data'])))

        self.dict_srv_wr = {}
        self.dict_srv_rd = {}

        for item in self.conf_data.keys():
            if self.conf_data[item].has_key('target_memory'):
                memory_name = self.conf_data[item]['target_memory']
                rospy.loginfo('\033[94m[%s]\033[0m wait for bringup %s...'%(rospy.get_name(), memory_name))

                rospy.wait_for_service('/%s/write_data'%memory_name)
                self.dict_srv_wr[memory_name] = rospy.ServiceProxy('/%s/write_data'%memory_name, WriteData)
                self.dict_srv_rd[memory_name] = rospy.ServiceProxy('/%s/read_data'%memory_name, ReadData)

                self.register_data_to_memory(memory_name, item, self.conf_data[item]['data'])

        self.is_enable_perception = True
        self.srv_start = rospy.Service('%s/start'%rospy.get_name(), Empty, self.handle_start_perception)
        self.srv_stop = rospy.Service('%s/stop'%rospy.get_name(), Empty, self.handle_stop_perception)

        self.pub_event = rospy.Publisher('forwarding_event', ForwardingEvent, queue_size=10)
        rospy.loginfo('\033[94m[%s]\033[0m initialize perception_base done...'%rospy.get_name())

    def handle_start_perception(self, req):
        self.is_enable_perception = True
        rospy.logdebug('%s is enabled...'%rospy.get_name())

    def handle_stop_perception(self, req):
        self.is_enable_perception = False
        rospy.logdebug('%s is disabled...'%rospy.get_name())

    def raise_event(self, perception_item, event):
        if not perception_item in self.conf_data.keys():
            rospy.logwarn('<%s> perception is not member of perception configuration...'%perception_item)
            return
        if not event in self.conf_data[perception_item]['events']:
            rospy.logwarn('<%s> event is not member of event list of perception configuration...'%event)
            return

        if not self.is_enable_perception:
            return

        msg = ForwardingEvent()
        msg.header.stamp = rospy.Time.now()
        msg.event = event
        msg.by = perception_item

        self.pub_event.publish(msg)

    def register_data_to_memory(self, memory_name, perception_name, data):
        rospy.wait_for_service('/%s/register_data'%memory_name)
        srv_register = rospy.ServiceProxy('/%s/register_data'%memory_name, RegisterData)

        srv_req = RegisterDataRequest()
        srv_req.perception_name = perception_name
        srv_req.data = json.dumps(data)

        return srv_register(srv_req)

    def save_to_memory(self, perception_name, data={}):
        if data == {}:
            rospy.logwarn('Empty data inserted...')
            return

        for item in data.keys():
            if not item in self.conf_data[perception_name]['data'].keys():
                rospy.logwarn('Wrong data inserted...')
                return

        #if not self.is_enable_perception:
        #    return

        srv_req = WriteDataRequest()
        srv_req.perception_name = perception_name
        srv_req.data = json.dumps(data)
        srv_req.by = rospy.get_name()

        target_memory = self.conf_data[perception_name]['target_memory']
        self.dict_srv_wr[target_memory](srv_req)

    def read_from_memory(self, target_memory, data):
        pass
<|endoftext|>


<commit_before>from flask_wtf import Form
from wtforms import StringField, SelectField, TextAreaField, SelectMultipleField, BooleanField
from wtforms.validators import DataRequired, Optional
from wtforms.fields.html5 import DateField, IntegerField
from wtforms.widgets import CheckboxInput

from octopus.user.models import User
from octopus.case.models import Region, CaseType, Case, Tag, CaseStaffMap

class NewCaseForm(Form):
    crd_number = IntegerField('CRD Number',
                              validators=[DataRequired()])
    case_name = StringField('Case Name', validators=[DataRequired()])
    case_desc = TextAreaField('Case Description')
    start_date = DateField('Start Date',
                           validators=[DataRequired()])
    end_date = DateField('End Date', validators=[Optional()])
    case_type = SelectField('Case Type', validators=[DataRequired()])

    case_region = SelectField('Regional Office', validators=[DataRequired()])
    case_lead = SelectField('Case Lead', validators=[DataRequired()])

    self_to_case = BooleanField('Add me to this case', default=True)

    def __init__(self, *args, **kwargs):
        super(NewCaseForm, self).__init__(*args, **kwargs)
        self.case_type.choices = [(unicode(i.id), i.code) for i in CaseType.query]
        self.case_region.choices = [(unicode(i.id), i.code) for i in Region.query]
        self.case_lead.choices = [(unicode(i.id), i.full_name) for i in User.query]

    def validate(self):
        initial_validation = super(NewCaseForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_new_case(self):
        case_type = CaseType.query.get(self.case_type.data)
        region = Region.query.get(self.case_region.data)
        case_lead = User.query.get(self.case_lead.data)

        case = Case.create(crd_number=self.crd_number.data,
                           case_name=self.case_name.data,
                           case_desc=self.case_desc.data,
                           start_date=self.start_date.data,
                           end_date=self.end_date.data,
                           case_type=case_type,
                           region=region,
        )

        # add case lead to staff table
        lead = CaseStaffMap.create(user_id=case_lead.id,
                                     case_id=case.id,
                                     primary=True)
        lead.save()
        return case

class EditCoreCaseForm(Form):
    crd_number = IntegerField('CRD Number',
                              validators=[Optional()])
    case_name = StringField('Case Name', validators=[Optional()])
    case_desc = TextAreaField('Case Description')
    start_date = DateField('Start Date',
                           validators=[Optional()])
    end_date = DateField('End Date', validators=[Optional()])
    case_type = SelectField('Case Type', validators=[Optional()])

    case_region = SelectField("Regional Office", validators=[Optional()])

    def __init__(self, case_id, *args, **kwargs):
        super(EditCoreCaseForm, self).__init__(*args, **kwargs)
        self.case_id = case_id
        self.current_case = Case.get_by_id(self.case_id)

        # This step is needed for bypassing a defaulting of the SelectField bug
        # we set the default by making the first element of the list the default value
        case_types = [(i.id, i.code) for i in CaseType.query]
        if self.current_case.case_type.id:
            for c, (i, d) in enumerate(case_types):
                if i == self.current_case.case_type.id:
                    case_types.insert(0, case_types.pop(c))
                    break
        self.case_type.choices = case_types
        regions = [(i.id, i.code) for i in Region.query]
        if self.current_case.region.id:
            for c, (i, d) in enumerate(regions):
                if i == self.current_case.region.id:
                    regions.insert(0, regions.pop(c))
        self.case_region.choices = regions

        self.crd_number.placeholder = self.current_case.crd_number if self.current_case.crd_number else None
        self.case_name.placeholder = self.current_case.case_name if self.current_case.case_name else None
        self.case_desc.placeholder = self.current_case.case_desc if self.current_case.case_desc else None
        self.start_date.placeholder = self.current_case.start_date if self.current_case.start_date else None
        self.end_date.placeholder = self.current_case.end_date if self.current_case.end_date else None

    def validate(self):
        initial_validation = super(EditCoreCaseForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_updates(self):
        if self.case_name.data:
            self.current_case.case_name = self.case_name.data
        if self.case_desc.data:
            self.current_case.case_desc = self.case_desc.data
        if self.crd_number.data:
            self.current_case.crd_number = self.crd_number.data
        if self.start_date.data:
            self.current_case.start_date = self.start_date.data
        if self.end_date.data:
            self.current_case.end_date = self.end_date.data
        if self.case_type.data:
            case_type = CaseType.query.get(self.case_type.data)
            self.current_case.case_type = case_type
        if self.case_region.data:
            region = Region.query.get(self.case_region.data)
            self.current_case.region = region

        self.current_case.save()

class CaseTagsForm(Form):
    case_tags = SelectMultipleField(label='Case Tags', validators=[Optional()])

    def __init__(self, case_id, kind, *args, **kwargs):
        """

        :param case_id: int
        :param kind: must be one of 'risk', 'non_qau_staff'
        """
        super(CaseTagsForm, self).__init__(*args, **kwargs)
        self.case_id = case_id
        if kind not in {'risk', 'non_qau_staff'}:
            raise ValueError('tag "kind" must be one of: "risk", "non_qau_staff"')
        self.tag_kind = kind
        self.case_tags.choices = [(i.tag, i.tag) for i in Case.get_by_id(case_id).tags if i.kind == self.tag_kind]
        self.tag_values = None

    def commit_updates(self):
        """
        type: IO ()
        commit updates to database
        :raise ValueError:
        :returns None
        """
        case = Case.get_by_id(self.case_id)

        tags = []
        if self.tag_values:
            for t in self.tag_values:
                tag = Tag.query.filter(Tag.kind == self.tag_kind, Tag.tag == t).first()
                if tag:
                    tags.append(tag)
                else:
                    tags.append(Tag.create(kind=self.tag_kind, tag=t))

        case.tags = tags + [i for i in Tag.query.filter(Tag.kind != self.tag_kind)]
        case.save()
        return None

    def validate(self):
        if self.case_tags.data:
            self.tag_values = set(self.case_tags.data)
        return True

class CaseStaffForm(Form):
    contractors = SelectMultipleField(label='QAU Contractor Resources', validators=[Optional()], coerce=int)
    qau_staff = SelectMultipleField(label='QAU Full Time Resources', validators=[Optional()], coerce=int)

    def __init__(self, case_id, *args, **kwargs):
        super(CaseStaffForm, self).__init__(*args, **kwargs)
        self.case_id = case_id
        self.contractors.choices = [(i.id, i.username) for i in User.query if i.is_contractor]
        self.qau_staff.choices = [(i.id, i.username) for i in User.query if i.is_permanent]

        # TODO: set defaults in choice fields. Need help with the association tables for this one
        # self.contractors.data = [unicode(i.id) for i in <query look up the id's of assigned staff> if i.is_contractor]
        # self.qau_staff.data = [unicode(i.id) for i in <query look up the id's of assigned staff> if i.is_permanent]
        # self.process()

    def validate(self):
        initial_validation = super(CaseStaffForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_updates(self):
        # TODO: Monica needs to figure out what to put here to get staff mapping to work
        print self.qau_staff.data
        print self.contractors.data
        return True

<commit_msg>form now pre-selects existing case staff<commit_after>from flask_wtf import Form
from wtforms import StringField, SelectField, TextAreaField, SelectMultipleField, BooleanField
from wtforms.validators import DataRequired, Optional
from wtforms.fields.html5 import DateField, IntegerField
from wtforms.widgets import CheckboxInput

from octopus.user.models import User
from octopus.case.models import Region, CaseType, Case, Tag, CaseStaffMap
from octopus.extensions import db

class NewCaseForm(Form):
    crd_number = IntegerField('CRD Number',
                              validators=[DataRequired()])
    case_name = StringField('Case Name', validators=[DataRequired()])
    case_desc = TextAreaField('Case Description')
    start_date = DateField('Start Date',
                           validators=[DataRequired()])
    end_date = DateField('End Date', validators=[Optional()])
    case_type = SelectField('Case Type', validators=[DataRequired()])

    case_region = SelectField('Regional Office', validators=[DataRequired()])
    case_lead = SelectField('Case Lead', validators=[DataRequired()])

    self_to_case = BooleanField('Add me to this case', default=True)

    def __init__(self, *args, **kwargs):
        super(NewCaseForm, self).__init__(*args, **kwargs)
        self.case_type.choices = [(unicode(i.id), i.code) for i in CaseType.query]
        self.case_region.choices = [(unicode(i.id), i.code) for i in Region.query]
        self.case_lead.choices = [(unicode(i.id), i.full_name) for i in User.query]

    def validate(self):
        initial_validation = super(NewCaseForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_new_case(self):
        case_type = CaseType.query.get(self.case_type.data)
        region = Region.query.get(self.case_region.data)
        case_lead = User.query.get(self.case_lead.data)

        case = Case.create(crd_number=self.crd_number.data,
                           case_name=self.case_name.data,
                           case_desc=self.case_desc.data,
                           start_date=self.start_date.data,
                           end_date=self.end_date.data,
                           case_type=case_type,
                           region=region,
        )

        # add case lead to staff table
        lead = CaseStaffMap.create(user_id=case_lead.id,
                                     case_id=case.id,
                                     primary=True)
        lead.save()
        return case

class EditCoreCaseForm(Form):
    crd_number = IntegerField('CRD Number',
                              validators=[Optional()])
    case_name = StringField('Case Name', validators=[Optional()])
    case_desc = TextAreaField('Case Description')
    start_date = DateField('Start Date',
                           validators=[Optional()])
    end_date = DateField('End Date', validators=[Optional()])
    case_type = SelectField('Case Type', validators=[Optional()])

    case_region = SelectField("Regional Office", validators=[Optional()])

    def __init__(self, case_id, *args, **kwargs):
        super(EditCoreCaseForm, self).__init__(*args, **kwargs)
        self.case_id = case_id
        self.current_case = Case.get_by_id(self.case_id)

        # This step is needed for bypassing a defaulting of the SelectField bug
        # we set the default by making the first element of the list the default value
        case_types = [(i.id, i.code) for i in CaseType.query]
        if self.current_case.case_type.id:
            for c, (i, d) in enumerate(case_types):
                if i == self.current_case.case_type.id:
                    case_types.insert(0, case_types.pop(c))
                    break
        self.case_type.choices = case_types
        regions = [(i.id, i.code) for i in Region.query]
        if self.current_case.region.id:
            for c, (i, d) in enumerate(regions):
                if i == self.current_case.region.id:
                    regions.insert(0, regions.pop(c))
        self.case_region.choices = regions

        self.crd_number.placeholder = self.current_case.crd_number if self.current_case.crd_number else None
        self.case_name.placeholder = self.current_case.case_name if self.current_case.case_name else None
        self.case_desc.placeholder = self.current_case.case_desc if self.current_case.case_desc else None
        self.start_date.placeholder = self.current_case.start_date if self.current_case.start_date else None
        self.end_date.placeholder = self.current_case.end_date if self.current_case.end_date else None

    def validate(self):
        initial_validation = super(EditCoreCaseForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_updates(self):
        if self.case_name.data:
            self.current_case.case_name = self.case_name.data
        if self.case_desc.data:
            self.current_case.case_desc = self.case_desc.data
        if self.crd_number.data:
            self.current_case.crd_number = self.crd_number.data
        if self.start_date.data:
            self.current_case.start_date = self.start_date.data
        if self.end_date.data:
            self.current_case.end_date = self.end_date.data
        if self.case_type.data:
            case_type = CaseType.query.get(self.case_type.data)
            self.current_case.case_type = case_type
        if self.case_region.data:
            region = Region.query.get(self.case_region.data)
            self.current_case.region = region

        self.current_case.save()

class CaseTagsForm(Form):
    case_tags = SelectMultipleField(label='Case Tags', validators=[Optional()])

    def __init__(self, case_id, kind, *args, **kwargs):
        """

        :param case_id: int
        :param kind: must be one of 'risk', 'non_qau_staff'
        """
        super(CaseTagsForm, self).__init__(*args, **kwargs)
        self.case_id = case_id
        if kind not in {'risk', 'non_qau_staff'}:
            raise ValueError('tag "kind" must be one of: "risk", "non_qau_staff"')
        self.tag_kind = kind
        self.case_tags.choices = [(i.tag, i.tag) for i in Case.get_by_id(case_id).tags if i.kind == self.tag_kind]
        self.tag_values = None

    def commit_updates(self):
        """
        type: IO ()
        commit updates to database
        :raise ValueError:
        :returns None
        """
        case = Case.get_by_id(self.case_id)

        tags = []
        if self.tag_values:
            for t in self.tag_values:
                tag = Tag.query.filter(Tag.kind == self.tag_kind, Tag.tag == t).first()
                if tag:
                    tags.append(tag)
                else:
                    tags.append(Tag.create(kind=self.tag_kind, tag=t))

        case.tags = tags + [i for i in Tag.query.filter(Tag.kind != self.tag_kind)]
        case.save()
        return None

    def validate(self):
        if self.case_tags.data:
            self.tag_values = set(self.case_tags.data)
        return True

class CaseStaffForm(Form):
    contractors = SelectMultipleField(label='QAU Contractor Resources', validators=[Optional()], coerce=int)
    qau_staff = SelectMultipleField(label='QAU Full Time Resources', validators=[Optional()], coerce=int)

    def __init__(self, case_id, *args, **kwargs):
        super(CaseStaffForm, self).__init__(*args, **kwargs)
        self.case_id = case_id

        self.contractors.choices = [(i.id, i.username) for i in User.query if i.is_contractor]
        self.qau_staff.choices = [(i.id, i.username) for i in User.query if i.is_permanent]

        staff = db.session.query(User).\
                    join('user_cases', 'case').\
                    filter(User.user_cases.any(case_id=case_id)).\
                    filter(CaseStaffMap.primary == 0).all()

        self.contractors.default = [str(i.id) for i in staff if i.is_contractor]
        self.qau_staff.default = [str(i.id) for i in staff if i.is_permanent]
        self.process()

    def validate(self):
        initial_validation = super(CaseStaffForm, self).validate()
        if not initial_validation:
            return False
        return True

    def commit_updates(self):
        # TODO: Monica needs to figure out what to put here to get staff mapping to work
        print self.qau_staff.data
        print self.contractors.data
        return True

<|endoftext|>