<commit_before>#   Copyright 2014-2018 PUNCH Cyber Analytics Group
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""
Overview
========

Sends content to STDOUT

"""

import stoq.helpers as helpers
from stoq.data_classes import StoqResponse
from stoq.plugins import ConnectorPlugin

class StdoutConnector(ConnectorPlugin):
    def save(self, response: StoqResponse) -> None:
        """
        Print results to STDOUT

        """
        print(helpers.dumps(response))
<commit_msg>Remove call to helpers.dumps<commit_after>#   Copyright 2014-2018 PUNCH Cyber Analytics Group
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

"""
Overview
========

Sends content to STDOUT

"""

from stoq.data_classes import StoqResponse
from stoq.plugins import ConnectorPlugin

class StdoutConnector(ConnectorPlugin):
    def save(self, response: StoqResponse) -> None:
        """
        Print results to STDOUT

        """
        print(response)
<|endoftext|>


<commit_before>from __future__ import absolute_import

import logging
import six

from sentry.exceptions import InvalidIdentity, PluginError
from sentry.models import Commit, Release, Repository, User
from sentry.plugins import bindings
from sentry.tasks.base import instrumented_task, retry

logger = logging.getLogger(__name__)

@instrumented_task(name='sentry.tasks.commits.fetch_commits', queue='commits',
                   default_retry_delay=60 * 5, max_retries=5)
@retry(exclude=(Release.DoesNotExist, User.DoesNotExist,))
def fetch_commits(release_id, user_id, refs, prev_release_id=None, **kwargs):
    commit_list = []

    release = Release.objects.get(id=release_id)
    user = User.objects.get(id=user_id)

    prev_release = None
    if prev_release_id is not None:
        try:
            prev_release = Release.objects.get(id=prev_release_id)
        except Release.DoesNotExist:
            pass

    for ref in refs:
        try:
            repo = Repository.objects.get(
                organization_id=release.organization_id,
                name=ref['repository'],
            )
        except Repository.DoesNotExist:
            continue

        try:
            commit = Commit.objects.get(
                organization_id=release.organization_id,
                repository_id=repo.id,
                key=ref['commit'],
            )
        except Commit.DoesNotExist:
            continue

        try:
            provider_cls = bindings.get('repository.provider').get(repo.provider)
        except KeyError:
            continue

        # if previous commit isn't provided, try to get from
        # previous release otherwise, give up
        if ref.get('previousCommit'):
            start_sha = ref['previousCommit']
        elif prev_release:
            try:
                start_sha = Commit.objects.filter(
                    organization_id=release.organization_id,
                    releaseheadcommit__release=prev_release,
                    repository_id=repo.id,
                ).values_list('key', flat=True)[0]
            except IndexError:
                continue
        else:
            continue

        end_sha = commit.key
        provider = provider_cls(id=repo.provider)
        try:
            repo_commits = provider.compare_commits(
                repo, start_sha, end_sha, actor=user
            )
        except NotImplementedError:
            pass
        except (PluginError, InvalidIdentity) as e:
            logger.exception(six.text_type(e))
        else:
            commit_list.extend(repo_commits)

    if commit_list:
        release.set_commits(commit_list)
<commit_msg>don't try to pull commit from db in fetch_commits (#5334)<commit_after>from __future__ import absolute_import

import logging
import six

from sentry.exceptions import InvalidIdentity, PluginError
from sentry.models import Commit, Release, Repository, User
from sentry.plugins import bindings
from sentry.tasks.base import instrumented_task, retry

logger = logging.getLogger(__name__)

@instrumented_task(name='sentry.tasks.commits.fetch_commits', queue='commits',
                   default_retry_delay=60 * 5, max_retries=5)
@retry(exclude=(Release.DoesNotExist, User.DoesNotExist,))
def fetch_commits(release_id, user_id, refs, prev_release_id=None, **kwargs):
    commit_list = []

    release = Release.objects.get(id=release_id)
    user = User.objects.get(id=user_id)

    prev_release = None
    if prev_release_id is not None:
        try:
            prev_release = Release.objects.get(id=prev_release_id)
        except Release.DoesNotExist:
            pass

    for ref in refs:
        try:
            repo = Repository.objects.get(
                organization_id=release.organization_id,
                name=ref['repository'],
            )
        except Repository.DoesNotExist:
            continue

        try:
            provider_cls = bindings.get('repository.provider').get(repo.provider)
        except KeyError:
            continue

        # if previous commit isn't provided, try to get from
        # previous release otherwise, give up
        if ref.get('previousCommit'):
            start_sha = ref['previousCommit']
        elif prev_release:
            try:
                start_sha = Commit.objects.filter(
                    organization_id=release.organization_id,
                    releaseheadcommit__release=prev_release,
                    repository_id=repo.id,
                ).values_list('key', flat=True)[0]
            except IndexError:
                continue
        else:
            continue

        end_sha = ref['commit']
        provider = provider_cls(id=repo.provider)
        try:
            repo_commits = provider.compare_commits(
                repo, start_sha, end_sha, actor=user
            )
        except NotImplementedError:
            pass
        except (PluginError, InvalidIdentity) as e:
            logger.exception(six.text_type(e))
        else:
            commit_list.extend(repo_commits)

    if commit_list:
        release.set_commits(commit_list)
<|endoftext|>


<commit_before>"""
This module implements the main Evennia server process, the core of
the game engine.

This module should be started with the 'twistd' executable since it
sets up all the networking features.  (this is done automatically
by game/evennia.py).

"""
import sys
import os
from src.server.webserver import EvenniaReverseProxyResource

if os.name == 'nt':
    # For Windows batchfile we need an extra path insertion here.
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
                os.path.dirname(os.path.abspath(__file__))))))

from twisted.application import internet, service
from twisted.internet import protocol, reactor
from twisted.web import server
from django.conf import settings
from src.utils.utils import get_evennia_version, mod_import, make_iter
from src.server.portal.portalsessionhandler import PORTAL_SESSIONS

PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]

if os.name == 'nt':
    # For Windows we need to handle pid files manually.
    PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, 'portal.pid')

#------------------------------------------------------------
# Evennia Portal settings
#------------------------------------------------------------

VERSION = get_evennia_version()

SERVERNAME = settings.SERVERNAME

PORTAL_RESTART = os.path.join(settings.GAME_DIR, 'portal.restart')

TELNET_PORTS = settings.TELNET_PORTS
SSL_PORTS = settings.SSL_PORTS
SSH_PORTS = settings.SSH_PORTS
WEBSERVER_PORTS = settings.WEBSERVER_PORTS

TELNET_INTERFACES = settings.TELNET_INTERFACES
SSL_INTERFACES = settings.SSL_INTERFACES
SSH_INTERFACES = settings.SSH_INTERFACES
WEBSERVER_INTERFACES = settings.WEBSERVER_INTERFACES

TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS and TELNET_INTERFACES
SSL_ENABLED = settings.SSL_ENABLED and SSL_PORTS and SSL_INTERFACES
SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS and SSH_INTERFACES
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED

AMP_HOST = settings.AMP_HOST
AMP_PORT = settings.AMP_PORT
AMP_INTERFACE = settings.AMP_INTERFACE
AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE

#------------------------------------------------------------
# Portal Service object
#------------------------------------------------------------
class Portal(object):

    """
    The main Portal server handler. This object sets up the database and
    tracks and interlinks all the twisted network services that make up
    Portal.
    """

    def __init__(self, application):
        """
        Setup the server.

        application - an instantiated Twisted application

        """
        sys.path.append('.')

        # create a store of services
        self.services = service.IServiceCollection(application)
        self.amp_protocol = None  # set by amp factory
        self.sessions = PORTAL_SESSIONS
        self.sessions.portal = self

        # set a callback if the server is killed abruptly,
        # by Ctrl-C, reboot etc.
        reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _reactor_stopping=True)

        self.game_running = False

    def set_restart_mode(self, mode=None):
        """
        This manages the flag file that tells the runner if the server should
        be restarted or is shutting down. Valid modes are True/False and None.
        If mode is None, no change will be done to the flag file.
        """
        if mode is None:
            return
        f = open(PORTAL_RESTART, 'w')
        print "writing mode=%(mode)s to %(portal_restart)s" % {'mode': mode, 'portal_restart': PORTAL_RESTART}
        f.write(str(mode))
        f.close()

    def shutdown(self, restart=None, _reactor_stopping=False):
        """
        Shuts down the server from inside it.

        restart - True/False sets the flags so the server will be
                  restarted or not. If None, the current flag setting
                  (set at initialization or previous runs) is used.
        _reactor_stopping - this is set if server is already in the process of
                  shutting down; in this case we don't need to stop it again.

        Note that restarting (regardless of the setting) will not work
        if the Portal is currently running in daemon mode. In that
        case it always needs to be restarted manually.
        """
        if _reactor_stopping and hasattr(self, "shutdown_complete"):
            # we get here due to us calling reactor.stop below. No need
            # to do the shutdown procedure again.
            return
        self.set_restart_mode(restart)
        if os.name == 'nt' and os.path.exists(PORTAL_PIDFILE):
            # for Windows we need to remove pid files manually
            os.remove(PORTAL_PIDFILE)
        if not _reactor_stopping:
            # shutting down the reactor will trigger another signal. We set
            # a flag to avoid loops.
            self.shutdown_complete = True
            reactor.callLater(0, reactor.stop)

#------------------------------------------------------------
#
# Start the Portal proxy server and add all active services
#
#------------------------------------------------------------

# twistd requires us to define the variable 'application' so it knows
# what to execute from.
application = service.Application('Portal')

# The main Portal server program. This sets up the database
# and is where we store all the other services.
PORTAL = Portal(application)

print '-' * 50
print ' %(servername)s Portal (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION}

if AMP_ENABLED:

    # The AMP protocol handles the communication between
    # the portal and the mud server. Only reason to ever deactivate
    # it would be during testing and debugging.

    from src.server import amp

    print '  amp (to Server): %s' % AMP_PORT

    factory = amp.AmpClientFactory(PORTAL)
    amp_client = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
    amp_client.setName('evennia_amp')
    PORTAL.services.addService(amp_client)

# We group all the various services under the same twisted app.
# These will gradually be started as they are initialized below.

if TELNET_ENABLED:

    # Start telnet game connections

    from src.server.portal import telnet

    for interface in TELNET_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(TELNET_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in TELNET_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = protocol.ServerFactory()
            factory.protocol = telnet.TelnetProtocol
            factory.sessionhandler = PORTAL_SESSIONS
            telnet_service = internet.TCPServer(port, factory, interface=interface)
            telnet_service.setName('EvenniaTelnet%s' % pstring)
            PORTAL.services.addService(telnet_service)

            print '  telnet%s: %s' % (ifacestr, port)

if SSL_ENABLED:

    # Start SSL game connection (requires PyOpenSSL).

    from src.server.portal import ssl

    for interface in SSL_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(SSL_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in SSL_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = protocol.ServerFactory()
            factory.sessionhandler = PORTAL_SESSIONS
            factory.protocol = ssl.SSLProtocol
            ssl_service = internet.SSLServer(port,
                                             factory,
                                             ssl.getSSLContext(),
                                             interface=interface)
            ssl_service.setName('EvenniaSSL%s' % pstring)
            PORTAL.services.addService(ssl_service)

            print "  ssl%s: %s" % (ifacestr, port)

if SSH_ENABLED:

    # Start SSH game connections. Will create a keypair in
    # evennia/game if necessary.

    from src.server.portal import ssh

    for interface in SSH_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(SSH_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in SSH_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = ssh.makeFactory({'protocolFactory': ssh.SshProtocol,
                                       'protocolArgs': (),
                                       'sessions': PORTAL_SESSIONS})
            ssh_service = internet.TCPServer(port, factory, interface=interface)
            ssh_service.setName('EvenniaSSH%s' % pstring)
            PORTAL.services.addService(ssh_service)

            print "  ssl%s: %s" % (ifacestr, port)

if WEBSERVER_ENABLED:

    # Start a reverse proxy to relay data to the Server-side webserver

    for interface in WEBSERVER_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(WEBSERVER_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for proxyport, serverport in WEBSERVER_PORTS:
            pstring = "%s:%s<->%s" % (ifacestr, proxyport, serverport)
            web_root = EvenniaReverseProxyResource('127.0.0.1', serverport, '')
            webclientstr = ""
            if WEBCLIENT_ENABLED:
                # create ajax client processes at /webclientdata
                from src.server.portal.webclient import WebClient
                webclient = WebClient()
                webclient.sessionhandler = PORTAL_SESSIONS
                web_root.putChild("webclientdata", webclient)
                webclientstr = "/client"

            web_root = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
            proxy_service = internet.TCPServer(proxyport,
                                               web_root,
                                               interface=interface)
            proxy_service.setName('EvenniaWebProxy%s' % pstring)
            PORTAL.services.addService(proxy_service)
            print "  webproxy%s%s:%s (<-> %s)" % (webclientstr, ifacestr, proxyport, serverport)

for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
    # external plugin services to start
    plugin_module.start_plugin_services(PORTAL)

print '-' * 50  # end of terminal output

if os.name == 'nt':
    # Windows only: Set PID file manually
    f = open(os.path.join(settings.GAME_DIR, 'portal.pid'), 'w')
    f.write(str(os.getpid()))
    f.close()
<commit_msg>Moved import in portal.py to after the manual path insert (Windows only), which possibly increase Windows compatability.<commit_after>"""
This module implements the main Evennia server process, the core of
the game engine.

This module should be started with the 'twistd' executable since it
sets up all the networking features.  (this is done automatically
by game/evennia.py).

"""

if os.name == 'nt':
    # For Windows batchfile we need an extra path insertion here.
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
                os.path.dirname(os.path.abspath(__file__))))))
import sys
import os
from src.server.webserver import EvenniaReverseProxyResource
from twisted.application import internet, service
from twisted.internet import protocol, reactor
from twisted.web import server
from django.conf import settings
from src.utils.utils import get_evennia_version, mod_import, make_iter
from src.server.portal.portalsessionhandler import PORTAL_SESSIONS

PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]

if os.name == 'nt':
    # For Windows we need to handle pid files manually.
    PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, 'portal.pid')

#------------------------------------------------------------
# Evennia Portal settings
#------------------------------------------------------------

VERSION = get_evennia_version()

SERVERNAME = settings.SERVERNAME

PORTAL_RESTART = os.path.join(settings.GAME_DIR, 'portal.restart')

TELNET_PORTS = settings.TELNET_PORTS
SSL_PORTS = settings.SSL_PORTS
SSH_PORTS = settings.SSH_PORTS
WEBSERVER_PORTS = settings.WEBSERVER_PORTS

TELNET_INTERFACES = settings.TELNET_INTERFACES
SSL_INTERFACES = settings.SSL_INTERFACES
SSH_INTERFACES = settings.SSH_INTERFACES
WEBSERVER_INTERFACES = settings.WEBSERVER_INTERFACES

TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS and TELNET_INTERFACES
SSL_ENABLED = settings.SSL_ENABLED and SSL_PORTS and SSL_INTERFACES
SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS and SSH_INTERFACES
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED

AMP_HOST = settings.AMP_HOST
AMP_PORT = settings.AMP_PORT
AMP_INTERFACE = settings.AMP_INTERFACE
AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE

#------------------------------------------------------------
# Portal Service object
#------------------------------------------------------------
class Portal(object):

    """
    The main Portal server handler. This object sets up the database and
    tracks and interlinks all the twisted network services that make up
    Portal.
    """

    def __init__(self, application):
        """
        Setup the server.

        application - an instantiated Twisted application

        """
        sys.path.append('.')

        # create a store of services
        self.services = service.IServiceCollection(application)
        self.amp_protocol = None  # set by amp factory
        self.sessions = PORTAL_SESSIONS
        self.sessions.portal = self

        # set a callback if the server is killed abruptly,
        # by Ctrl-C, reboot etc.
        reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _reactor_stopping=True)

        self.game_running = False

    def set_restart_mode(self, mode=None):
        """
        This manages the flag file that tells the runner if the server should
        be restarted or is shutting down. Valid modes are True/False and None.
        If mode is None, no change will be done to the flag file.
        """
        if mode is None:
            return
        f = open(PORTAL_RESTART, 'w')
        print "writing mode=%(mode)s to %(portal_restart)s" % {'mode': mode, 'portal_restart': PORTAL_RESTART}
        f.write(str(mode))
        f.close()

    def shutdown(self, restart=None, _reactor_stopping=False):
        """
        Shuts down the server from inside it.

        restart - True/False sets the flags so the server will be
                  restarted or not. If None, the current flag setting
                  (set at initialization or previous runs) is used.
        _reactor_stopping - this is set if server is already in the process of
                  shutting down; in this case we don't need to stop it again.

        Note that restarting (regardless of the setting) will not work
        if the Portal is currently running in daemon mode. In that
        case it always needs to be restarted manually.
        """
        if _reactor_stopping and hasattr(self, "shutdown_complete"):
            # we get here due to us calling reactor.stop below. No need
            # to do the shutdown procedure again.
            return
        self.set_restart_mode(restart)
        if os.name == 'nt' and os.path.exists(PORTAL_PIDFILE):
            # for Windows we need to remove pid files manually
            os.remove(PORTAL_PIDFILE)
        if not _reactor_stopping:
            # shutting down the reactor will trigger another signal. We set
            # a flag to avoid loops.
            self.shutdown_complete = True
            reactor.callLater(0, reactor.stop)

#------------------------------------------------------------
#
# Start the Portal proxy server and add all active services
#
#------------------------------------------------------------

# twistd requires us to define the variable 'application' so it knows
# what to execute from.
application = service.Application('Portal')

# The main Portal server program. This sets up the database
# and is where we store all the other services.
PORTAL = Portal(application)

print '-' * 50
print ' %(servername)s Portal (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION}

if AMP_ENABLED:

    # The AMP protocol handles the communication between
    # the portal and the mud server. Only reason to ever deactivate
    # it would be during testing and debugging.

    from src.server import amp

    print '  amp (to Server): %s' % AMP_PORT

    factory = amp.AmpClientFactory(PORTAL)
    amp_client = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
    amp_client.setName('evennia_amp')
    PORTAL.services.addService(amp_client)

# We group all the various services under the same twisted app.
# These will gradually be started as they are initialized below.

if TELNET_ENABLED:

    # Start telnet game connections

    from src.server.portal import telnet

    for interface in TELNET_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(TELNET_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in TELNET_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = protocol.ServerFactory()
            factory.protocol = telnet.TelnetProtocol
            factory.sessionhandler = PORTAL_SESSIONS
            telnet_service = internet.TCPServer(port, factory, interface=interface)
            telnet_service.setName('EvenniaTelnet%s' % pstring)
            PORTAL.services.addService(telnet_service)

            print '  telnet%s: %s' % (ifacestr, port)

if SSL_ENABLED:

    # Start SSL game connection (requires PyOpenSSL).

    from src.server.portal import ssl

    for interface in SSL_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(SSL_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in SSL_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = protocol.ServerFactory()
            factory.sessionhandler = PORTAL_SESSIONS
            factory.protocol = ssl.SSLProtocol
            ssl_service = internet.SSLServer(port,
                                             factory,
                                             ssl.getSSLContext(),
                                             interface=interface)
            ssl_service.setName('EvenniaSSL%s' % pstring)
            PORTAL.services.addService(ssl_service)

            print "  ssl%s: %s" % (ifacestr, port)

if SSH_ENABLED:

    # Start SSH game connections. Will create a keypair in
    # evennia/game if necessary.

    from src.server.portal import ssh

    for interface in SSH_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(SSH_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for port in SSH_PORTS:
            pstring = "%s:%s" % (ifacestr, port)
            factory = ssh.makeFactory({'protocolFactory': ssh.SshProtocol,
                                       'protocolArgs': (),
                                       'sessions': PORTAL_SESSIONS})
            ssh_service = internet.TCPServer(port, factory, interface=interface)
            ssh_service.setName('EvenniaSSH%s' % pstring)
            PORTAL.services.addService(ssh_service)

            print "  ssl%s: %s" % (ifacestr, port)

if WEBSERVER_ENABLED:

    # Start a reverse proxy to relay data to the Server-side webserver

    for interface in WEBSERVER_INTERFACES:
        if ":" in interface:
            print "  iPv6 interfaces not yet supported"
            continue
        ifacestr = ""
        if interface != '0.0.0.0' or len(WEBSERVER_INTERFACES) > 1:
            ifacestr = "-%s" % interface
        for proxyport, serverport in WEBSERVER_PORTS:
            pstring = "%s:%s<->%s" % (ifacestr, proxyport, serverport)
            web_root = EvenniaReverseProxyResource('127.0.0.1', serverport, '')
            webclientstr = ""
            if WEBCLIENT_ENABLED:
                # create ajax client processes at /webclientdata
                from src.server.portal.webclient import WebClient
                webclient = WebClient()
                webclient.sessionhandler = PORTAL_SESSIONS
                web_root.putChild("webclientdata", webclient)
                webclientstr = "/client"

            web_root = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
            proxy_service = internet.TCPServer(proxyport,
                                               web_root,
                                               interface=interface)
            proxy_service.setName('EvenniaWebProxy%s' % pstring)
            PORTAL.services.addService(proxy_service)
            print "  webproxy%s%s:%s (<-> %s)" % (webclientstr, ifacestr, proxyport, serverport)

for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
    # external plugin services to start
    plugin_module.start_plugin_services(PORTAL)

print '-' * 50  # end of terminal output

if os.name == 'nt':
    # Windows only: Set PID file manually
    f = open(os.path.join(settings.GAME_DIR, 'portal.pid'), 'w')
    f.write(str(os.getpid()))
    f.close()
<|endoftext|>


<commit_before><commit_msg>view Ordens: mostrando status da interação<commit_after><|endoftext|>


<commit_before>from webWrapper import RestWrapper

import json
import logging
import datetime
import pytz
import copy
from decimal import Decimal, ROUND_HALF_UP

_logger = logging.getLogger()

class OttoBroker():
    def __init__(self, webWrapper, db, broker_id, tip_verifier, exchange_rate, tip_command):
        self._rest = RestWrapper(webWrapper,
            "https://api.iextrading.com/1.0", {})
        self._db = db
        self._user_cache = {}
        self._user_stocks = {}
        self._tip_verifier = tip_verifier
        self._exchange_rate = Decimal(exchange_rate)
        self._broker_id = broker_id
        self._tip_command = tip_command

        self._command_mapping = {
            'register': self._handle_register,
            'balance': self._handle_balance,
            'liststocks': self._handle_list_stocks,
            'buystock': self._handle_buy_stock,
            'sellstock': self._handle_sell_stock,
            'withdraw': self._handle_withdraw_command
        }

        self._populate_user_cache()

    @staticmethod
    def is_market_live(time=None):
        if time is None:
            time = datetime.datetime.now(pytz.timezone('EST5EDT'))

        return (time.weekday() < 5) and ((time.hour > 9 or (time.hour == 9 and time.minute >= 30)) and time.hour < 16)

    @staticmethod
    def _get_int(string):
        """
        This exists only to provide a nicer error message when converting ints. Allows for a more
        streamlined calling structure
        """
        try:
            return int(string)
        except Exception:
            raise Exception('Couldn\'t convert {} to an integer'.format(string))

    async def _get_stock_value(self, symbol_list):
        try:
            response = await self._rest.request('/stock/market/batch/', {'types': 'quote', 'symbols': ','.join(symbol_list)})
            unparsed = await response.text()
            data = None
            try:
                data = json.loads(unparsed)
            except Exception:
                raise Exception('Invalid API response: {}'.format(unparsed))
            if data is None:
                raise Exception('Got None from api response')
            elif not isinstance(data, dict):
                raise Exception('Unexpected data type ' + str(type(data)))

            unknown_symbols = []
            known_symbols = {}
            try:
                for symbol in symbol_list:
                    if symbol not in data:
                        unknown_symbols.append(symbol)
                    else:
                        known_symbols[symbol] = data[symbol]['quote']['latestPrice']
            except Exception:
                raise Exception('Unexpected response format')

            if not len(known_symbols):
                raise Exception('Couldn\'t find values for symbols: {}'.format(unknown_symbols))

            mistyped_symbols = {}
            for symbol in known_symbols:
                if not isinstance(known_symbols[symbol], Decimal):
                    try:
                        known_symbols[symbol] = Decimal(known_symbols[symbol])
                    except Exception:
                        mistyped_symbols[symbol] = known_symbols[symbol]
                        del known_symbols[symbol]

            if not len(known_symbols):
                error_message = ''
                if unknown_symbols:
                    error_message += 'Couldn\'t find values for symbols: {}'.format(unknown_symbols)
                if mistyped_symbols:
                    if error_message:
                        error_message += '. '
                    error_message += 'Couldn\'t find types for: {}'.format(
                        ','.join(
                            [':'.join([k, mistyped_symbols[k]]) for k in mistyped_symbols]
                        )
                    )
                raise Exception(error_message)

            return known_symbols, unknown_symbols, mistyped_symbols
        except Exception as e:
            raise Exception('Couldn\'t get stock value: {}'.format(str(e)))

    def _populate_user_cache(self):
        self._user_cache = {}
        user_list = self._db.broker_get_all_users()

        for user in user_list:
            self._user_cache[user.id] = user
            self._user_stocks[user.id] = self._load_user_stocks(user.id)

    def _update_single_user(self, user_id):
        user = self._db.broker_get_single_user(user_id)
        self._user_cache[user.id] = user
        self._user_stocks[user.id] = self._load_user_stocks(user.id)

    def _load_user_stocks(self, user_id):
        stock_list = self._db.broker_get_stocks_by_user(user_id)

        # create a dictionary of the stocks, grouped by ticker
        stock_dict = {}
        for stock in stock_list:
            if stock.ticker_symbol in stock_dict:
                stock_dict[stock.ticker_symbol].append(stock)
            else:
                stock_dict[stock.ticker_symbol] = [stock]

        return stock_dict

    def _get_user(self, user_id):
        if user_id in self._user_cache:
            return self._user_cache[user_id]
        else:
            raise Exception('You dn\'t have an account. Create one with `$broker register`')

    async def _buy_regular_stock(self, user_id, user_display_name, symbol, per_stock_cost, quantity):
        if not self.is_market_live():
            raise Exception('Can\'t trade after hours')
        # make the transaction, and report success
        result = self._db.broker_buy_regular_stock(user_id, symbol, per_stock_cost, quantity)
        if result is not None:
            # if we succeeded, update the cached user
            self._update_single_user(user_id)
            return 'Congratulations {}, you\'re the proud new owner of {} additional {} stocks'.format(user_display_name, quantity, symbol)
        raise Exception('Sorry {}, something went wrong in the database. Go yell at :otto:'.format(user_display_name))

    async def _handle_buy_stock(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 4:
                raise Exception('Sorry, you don\'t seem to have enough values in your message for me to parse.')
            symbol = command_args[2].upper()
            quantity = self._get_int(command_args[3])
            # here, since there's only one value, we can assume that if there was no exception, we got the value
            stock_vals, _, _ = await self._get_stock_value([symbol])
            per_stock_cost = stock_vals[symbol]

            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            if cur_user.balance < (quantity * per_stock_cost):
                raise Exception('Sorry {}, you don\'t have sufficient funds ({}) to buy {} {} stocks at {}'.format(cur_user.display_name,
                    quantity * per_stock_cost, quantity, symbol, per_stock_cost))

            return (await self._buy_regular_stock(cur_user.id, cur_user.display_name, symbol, per_stock_cost, quantity), True)
        except Exception as e:
            return ('No transaction occured. {}'.format(str(e)), False)

    async def _sell_regular_stock(self, user_id, user_display_name, symbol, per_stock_cost, quantity):
        if not self.is_market_live():
            raise Exception('Can\'t trade after hours')
        result = self._db.broker_sell_stock(user_id, symbol, per_stock_cost, quantity)
        if result is not None:
            # if we succeeded, update the cached user
            self._update_single_user(user_id)
            cur_user = self._user_cache[user_id]
            return 'Congratulations {}, your new balance is {}'.format(user_display_name, cur_user.balance)
        raise Exception('No transaction occurred. Sorry {}, something went wrong trying to sell the stocks. Go yell at :otto:'.format(user_display_name))

    async def _handle_sell_stock(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 4:
                return ('Sorry, you don\'t seem to have enough values in your message for me to parse.', False)
            symbol = command_args[2].upper()
            quantity = self._get_int(command_args[3])
            # here, since there's only one value, we can assume that if there was no exception, we got the value
            stock_vals, _, _ = await self._get_stock_value([symbol])
            per_stock_cost = stock_vals[symbol]

            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            cur_stocks = 0
            if symbol in self._user_stocks[message_author.id]:
                cur_stocks = len(self._user_stocks[message_author.id][symbol])
            if quantity > cur_stocks:
                raise Exception('Sorry {}, you only have {} {} stocks'.format(cur_user.display_name, cur_stocks, symbol))

            return (await self._sell_regular_stock(cur_user.id, cur_user.display_name, symbol, per_stock_cost, quantity), True)
        except Exception as e:
            return ('No transaction occurred. {}'.format(str(e)), False)

    async def _handle_list_stocks(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            stock_string = ''
            for symbol in self._user_stocks[user.id]:
                stock_string += '{} {} stocks, '.format(len(self._user_stocks[user.id][symbol]), symbol.upper())
            if stock_string:
                stock_string = stock_string[:-2]
                return ('{}, you have the following stocks: {}'.format(user.display_name, stock_string), True)
            else:
                return ('{}, you have no stocks!'.format(user.display_name), True)
        except Exception as e:
            return ('Could not list stocks: {}'.format(str(e)), False)

    async def _handle_register(self, command_args, message_author):
        if message_author.id in self._user_cache:
            return ('User {} already exists'.format(self._user_cache[message_author.id].display_name), False)
        self._db.broker_create_user(message_author.id, message_author.name)
        self._update_single_user(message_author.id)
        new_user = self._user_cache[message_author.id]
        return ('Welcome, {}. You have a starting balance of {}'.format(new_user.display_name, new_user.balance), True)

    async def _handle_balance(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)

            result_lines = [
                ['Capital', user.balance, '']
            ]
            errors = []
            total = user.balance

            stock_counts = {}
            stock_symbols = []
            for stock in self._user_stocks[user.id]:
                stock_symbols.append(stock)
                stock_counts[stock] = len(self._user_stocks[user.id][stock])
            if stock_symbols:
                stock_vals, unknown_vals, mistyped_vals = await self._get_stock_value(stock_symbols)

                if unknown_vals:
                    errors.append('The following stocks had unknown values: {}'.format(unknown_vals))

                if mistyped_vals:
                    errors.append('The following stock values could not be converted: {}'.format(mistyped_vals))

                for stock in stock_vals:
                    full_stock_value = stock_vals[stock] * stock_counts[stock]
                    full_stock_value = Decimal(full_stock_value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))
                    total += full_stock_value

                    purchase_stock_value = Decimal(0)
                    for owned_stock in self._user_stocks[user.id][stock]:
                        purchase_stock_value += owned_stock.purchase_cost

                    result_lines.append([
                        str(stock_counts[stock]) + ' ' + stock,
                        full_stock_value,
                        (Decimal(100) * (full_stock_value - purchase_stock_value) / purchase_stock_value).quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
                    ])

            result_lines.append(['Total', total, ''])

            if errors:
                result_lines.append(['Errors', ', '.join(errors), ''])

            prefix_len = max([len(x[0]) for x in result_lines])
            amt_len = max([len(str(x[1])) for x in result_lines])
            try:
                pct_gain_len = max([len(str(abs(x[2])))for x in result_lines if isinstance(x[2], Decimal)])
            except Exception:
                # no stocks means max([]), which is an error
                # just set it to 0
                pct_gain_len = 0

            result = []
            for line in result_lines:
                if line[2]:
                    result.append('{} : {} ({} {} %)'.format(
                        line[0].ljust(prefix_len),
                        str(line[1]).rjust(amt_len),
                        '+' if line[2] >= 0 else '-',
                        str(abs(line[2])).rjust(pct_gain_len)
                    ))
                else:
                    result.append('{} : {}'.format(
                        line[0].ljust(prefix_len),
                        str(line[1]).rjust(amt_len),
                    ))

            result = '\n'.join(result)

            return ('{}, your balance is:\n`{}`'.format(user.display_name, result), True)
        except Exception as e:
            _logger.exception(e)
            return ('Could not report balance: {}'.format(str(e)), False)

    async def _handle_withdraw_command(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 3:
                raise Exception('Sorry, you don\'t seem to have enough values in your message for me to parse.')
            amount = Decimal(command_args[2])
            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            if cur_user.balance < amount:
                raise Exception('Sorry {}, you have {}, not {}'.format(cur_user.display_name, cur_user.balance, amount))

            momocoin_amount = amount / self._exchange_rate
            momocoin_amount = Decimal(momocoin_amount.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))

            self._db.broker_give_money_to_user(cur_user.id, -amount, 'Withdrawal to Momocoins')
            self._update_single_user(cur_user.id)
            return (self._tip_command.format(message_author.mention, momocoin_amount), True)
        except Exception as e:
            return ('No withdrawal occurred. {}'.format(str(e)), False)

    async def handle_command(self, request_id, response_id, message, bot, parser, web):
        command_args = message.content.split(' ')
        # assumption, first value in message is '$broker'
        if len(command_args) < 2:
            return ('Specify a broker operation, please', False)

        command = command_args[1]

        if command in self._command_mapping:
            return await self._command_mapping[command](command_args, message.author)
        else:
            return ('Did not recognize command: ' + command, False)

    async def check_for_tips(self, message):
        if message.author.id == self._tip_verifier:
            # if we have a message from the tip verifying user (mimibot)
            # then try to parse out whether they're reporting a tip. and if so, was it to ottobot?
            try:
                if message.content.startswith('Tip completed.'):
                    tip_info = message.content.split('{')[1].strip('}')
                    # this should now be 'sender_id>receiver_id:amount'
                    tip_info = tip_info.split('>')
                    sender = tip_info[0]
                    receiver = tip_info[1].split(':')[0]
                    if receiver == self._broker_id:
                        try:
                            self._get_user(sender)
                        except Exception as e:
                            return 'Ottobot thanks you for your generousity, unregistered user'

                        amount = Decimal(tip_info[1].split(':')[1])
                        amount = self._exchange_rate * amount
                        amount = Decimal(amount.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))

                        if amount != 0:
                            self._db.broker_give_money_to_user(sender, amount, 'Tipping Ottobot')
                            self._update_single_user(sender)
                            return 'Ottobot winks at you, and walks away whistling. Your pockets feel heavier.'
                        else:
                            return 'No foolerino'

            except Exception as e:
                _logger.error('Failed to process tip:({})'.format(message.content))
                _logger.exception(e)
                return 'Failed to process tip({}) Go yell at :otto:'.format(e)<commit_msg>adding more information to tip response, and changing rounding, per pull request comments<commit_after>from webWrapper import RestWrapper

import json
import logging
import datetime
import pytz
import copy
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN

_logger = logging.getLogger()

class OttoBroker():
    def __init__(self, webWrapper, db, broker_id, tip_verifier, exchange_rate, tip_command):
        self._rest = RestWrapper(webWrapper,
            "https://api.iextrading.com/1.0", {})
        self._db = db
        self._user_cache = {}
        self._user_stocks = {}
        self._tip_verifier = tip_verifier
        self._exchange_rate = Decimal(exchange_rate)
        self._broker_id = broker_id
        self._tip_command = tip_command

        self._command_mapping = {
            'register': self._handle_register,
            'balance': self._handle_balance,
            'liststocks': self._handle_list_stocks,
            'buystock': self._handle_buy_stock,
            'sellstock': self._handle_sell_stock,
            'withdraw': self._handle_withdraw_command
        }

        self._populate_user_cache()

    @staticmethod
    def is_market_live(time=None):
        if time is None:
            time = datetime.datetime.now(pytz.timezone('EST5EDT'))

        return (time.weekday() < 5) and ((time.hour > 9 or (time.hour == 9 and time.minute >= 30)) and time.hour < 16)

    @staticmethod
    def _get_int(string):
        """
        This exists only to provide a nicer error message when converting ints. Allows for a more
        streamlined calling structure
        """
        try:
            return int(string)
        except Exception:
            raise Exception('Couldn\'t convert {} to an integer'.format(string))

    async def _get_stock_value(self, symbol_list):
        try:
            response = await self._rest.request('/stock/market/batch/', {'types': 'quote', 'symbols': ','.join(symbol_list)})
            unparsed = await response.text()
            data = None
            try:
                data = json.loads(unparsed)
            except Exception:
                raise Exception('Invalid API response: {}'.format(unparsed))
            if data is None:
                raise Exception('Got None from api response')
            elif not isinstance(data, dict):
                raise Exception('Unexpected data type ' + str(type(data)))

            unknown_symbols = []
            known_symbols = {}
            try:
                for symbol in symbol_list:
                    if symbol not in data:
                        unknown_symbols.append(symbol)
                    else:
                        known_symbols[symbol] = data[symbol]['quote']['latestPrice']
            except Exception:
                raise Exception('Unexpected response format')

            if not len(known_symbols):
                raise Exception('Couldn\'t find values for symbols: {}'.format(unknown_symbols))

            mistyped_symbols = {}
            for symbol in known_symbols:
                if not isinstance(known_symbols[symbol], Decimal):
                    try:
                        known_symbols[symbol] = Decimal(known_symbols[symbol])
                    except Exception:
                        mistyped_symbols[symbol] = known_symbols[symbol]
                        del known_symbols[symbol]

            if not len(known_symbols):
                error_message = ''
                if unknown_symbols:
                    error_message += 'Couldn\'t find values for symbols: {}'.format(unknown_symbols)
                if mistyped_symbols:
                    if error_message:
                        error_message += '. '
                    error_message += 'Couldn\'t find types for: {}'.format(
                        ','.join(
                            [':'.join([k, mistyped_symbols[k]]) for k in mistyped_symbols]
                        )
                    )
                raise Exception(error_message)

            return known_symbols, unknown_symbols, mistyped_symbols
        except Exception as e:
            raise Exception('Couldn\'t get stock value: {}'.format(str(e)))

    def _populate_user_cache(self):
        self._user_cache = {}
        user_list = self._db.broker_get_all_users()

        for user in user_list:
            self._user_cache[user.id] = user
            self._user_stocks[user.id] = self._load_user_stocks(user.id)

    def _update_single_user(self, user_id):
        user = self._db.broker_get_single_user(user_id)
        self._user_cache[user.id] = user
        self._user_stocks[user.id] = self._load_user_stocks(user.id)

    def _load_user_stocks(self, user_id):
        stock_list = self._db.broker_get_stocks_by_user(user_id)

        # create a dictionary of the stocks, grouped by ticker
        stock_dict = {}
        for stock in stock_list:
            if stock.ticker_symbol in stock_dict:
                stock_dict[stock.ticker_symbol].append(stock)
            else:
                stock_dict[stock.ticker_symbol] = [stock]

        return stock_dict

    def _get_user(self, user_id):
        if user_id in self._user_cache:
            return self._user_cache[user_id]
        else:
            raise Exception('You dn\'t have an account. Create one with `$broker register`')

    async def _buy_regular_stock(self, user_id, user_display_name, symbol, per_stock_cost, quantity):
        if not self.is_market_live():
            raise Exception('Can\'t trade after hours')
        # make the transaction, and report success
        result = self._db.broker_buy_regular_stock(user_id, symbol, per_stock_cost, quantity)
        if result is not None:
            # if we succeeded, update the cached user
            self._update_single_user(user_id)
            return 'Congratulations {}, you\'re the proud new owner of {} additional {} stocks'.format(user_display_name, quantity, symbol)
        raise Exception('Sorry {}, something went wrong in the database. Go yell at :otto:'.format(user_display_name))

    async def _handle_buy_stock(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 4:
                raise Exception('Sorry, you don\'t seem to have enough values in your message for me to parse.')
            symbol = command_args[2].upper()
            quantity = self._get_int(command_args[3])
            # here, since there's only one value, we can assume that if there was no exception, we got the value
            stock_vals, _, _ = await self._get_stock_value([symbol])
            per_stock_cost = stock_vals[symbol]

            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            if cur_user.balance < (quantity * per_stock_cost):
                raise Exception('Sorry {}, you don\'t have sufficient funds ({}) to buy {} {} stocks at {}'.format(cur_user.display_name,
                    quantity * per_stock_cost, quantity, symbol, per_stock_cost))

            return (await self._buy_regular_stock(cur_user.id, cur_user.display_name, symbol, per_stock_cost, quantity), True)
        except Exception as e:
            return ('No transaction occured. {}'.format(str(e)), False)

    async def _sell_regular_stock(self, user_id, user_display_name, symbol, per_stock_cost, quantity):
        if not self.is_market_live():
            raise Exception('Can\'t trade after hours')
        result = self._db.broker_sell_stock(user_id, symbol, per_stock_cost, quantity)
        if result is not None:
            # if we succeeded, update the cached user
            self._update_single_user(user_id)
            cur_user = self._user_cache[user_id]
            return 'Congratulations {}, your new balance is {}'.format(user_display_name, cur_user.balance)
        raise Exception('No transaction occurred. Sorry {}, something went wrong trying to sell the stocks. Go yell at :otto:'.format(user_display_name))

    async def _handle_sell_stock(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 4:
                return ('Sorry, you don\'t seem to have enough values in your message for me to parse.', False)
            symbol = command_args[2].upper()
            quantity = self._get_int(command_args[3])
            # here, since there's only one value, we can assume that if there was no exception, we got the value
            stock_vals, _, _ = await self._get_stock_value([symbol])
            per_stock_cost = stock_vals[symbol]

            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            cur_stocks = 0
            if symbol in self._user_stocks[message_author.id]:
                cur_stocks = len(self._user_stocks[message_author.id][symbol])
            if quantity > cur_stocks:
                raise Exception('Sorry {}, you only have {} {} stocks'.format(cur_user.display_name, cur_stocks, symbol))

            return (await self._sell_regular_stock(cur_user.id, cur_user.display_name, symbol, per_stock_cost, quantity), True)
        except Exception as e:
            return ('No transaction occurred. {}'.format(str(e)), False)

    async def _handle_list_stocks(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            stock_string = ''
            for symbol in self._user_stocks[user.id]:
                stock_string += '{} {} stocks, '.format(len(self._user_stocks[user.id][symbol]), symbol.upper())
            if stock_string:
                stock_string = stock_string[:-2]
                return ('{}, you have the following stocks: {}'.format(user.display_name, stock_string), True)
            else:
                return ('{}, you have no stocks!'.format(user.display_name), True)
        except Exception as e:
            return ('Could not list stocks: {}'.format(str(e)), False)

    async def _handle_register(self, command_args, message_author):
        if message_author.id in self._user_cache:
            return ('User {} already exists'.format(self._user_cache[message_author.id].display_name), False)
        self._db.broker_create_user(message_author.id, message_author.name)
        self._update_single_user(message_author.id)
        new_user = self._user_cache[message_author.id]
        return ('Welcome, {}. You have a starting balance of {}'.format(new_user.display_name, new_user.balance), True)

    async def _handle_balance(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)

            result_lines = [
                ['Capital', user.balance, '']
            ]
            errors = []
            total = user.balance

            stock_counts = {}
            stock_symbols = []
            for stock in self._user_stocks[user.id]:
                stock_symbols.append(stock)
                stock_counts[stock] = len(self._user_stocks[user.id][stock])
            if stock_symbols:
                stock_vals, unknown_vals, mistyped_vals = await self._get_stock_value(stock_symbols)

                if unknown_vals:
                    errors.append('The following stocks had unknown values: {}'.format(unknown_vals))

                if mistyped_vals:
                    errors.append('The following stock values could not be converted: {}'.format(mistyped_vals))

                for stock in stock_vals:
                    full_stock_value = stock_vals[stock] * stock_counts[stock]
                    full_stock_value = Decimal(full_stock_value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))
                    total += full_stock_value

                    purchase_stock_value = Decimal(0)
                    for owned_stock in self._user_stocks[user.id][stock]:
                        purchase_stock_value += owned_stock.purchase_cost

                    result_lines.append([
                        str(stock_counts[stock]) + ' ' + stock,
                        full_stock_value,
                        (Decimal(100) * (full_stock_value - purchase_stock_value) / purchase_stock_value).quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
                    ])

            result_lines.append(['Total', total, ''])

            if errors:
                result_lines.append(['Errors', ', '.join(errors), ''])

            prefix_len = max([len(x[0]) for x in result_lines])
            amt_len = max([len(str(x[1])) for x in result_lines])
            try:
                pct_gain_len = max([len(str(abs(x[2])))for x in result_lines if isinstance(x[2], Decimal)])
            except Exception:
                # no stocks means max([]), which is an error
                # just set it to 0
                pct_gain_len = 0

            result = []
            for line in result_lines:
                if line[2]:
                    result.append('{} : {} ({} {} %)'.format(
                        line[0].ljust(prefix_len),
                        str(line[1]).rjust(amt_len),
                        '+' if line[2] >= 0 else '-',
                        str(abs(line[2])).rjust(pct_gain_len)
                    ))
                else:
                    result.append('{} : {}'.format(
                        line[0].ljust(prefix_len),
                        str(line[1]).rjust(amt_len),
                    ))

            result = '\n'.join(result)

            return ('{}, your balance is:\n`{}`'.format(user.display_name, result), True)
        except Exception as e:
            _logger.exception(e)
            return ('Could not report balance: {}'.format(str(e)), False)

    async def _handle_withdraw_command(self, command_args, message_author):
        try:
            user = self._get_user(message_author.id)
            if len(command_args) < 3:
                raise Exception('Sorry, you don\'t seem to have enough values in your message for me to parse.')
            amount = Decimal(command_args[2])
            # make sure the user can afford the transaction
            cur_user = self._user_cache[message_author.id]
            if cur_user.balance < amount:
                raise Exception('Sorry {}, you have {}, not {}'.format(cur_user.display_name, cur_user.balance, amount))

            momocoin_amount = amount / self._exchange_rate
            momocoin_amount = Decimal(momocoin_amount.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))

            self._db.broker_give_money_to_user(cur_user.id, -amount, 'Withdrawal to Momocoins')
            self._update_single_user(cur_user.id)
            return (self._tip_command.format(message_author.mention, momocoin_amount), True)
        except Exception as e:
            return ('No withdrawal occurred. {}'.format(str(e)), False)

    async def handle_command(self, request_id, response_id, message, bot, parser, web):
        command_args = message.content.split(' ')
        # assumption, first value in message is '$broker'
        if len(command_args) < 2:
            return ('Specify a broker operation, please', False)

        command = command_args[1]

        if command in self._command_mapping:
            return await self._command_mapping[command](command_args, message.author)
        else:
            return ('Did not recognize command: ' + command, False)

    async def check_for_tips(self, message):
        if message.author.id == self._tip_verifier:
            # if we have a message from the tip verifying user (mimibot)
            # then try to parse out whether they're reporting a tip. and if so, was it to ottobot?
            try:
                if message.content.startswith('Tip completed.'):
                    tip_info = message.content.split('{')[1].strip('}')
                    # this should now be 'sender_id>receiver_id:amount'
                    tip_info = tip_info.split('>')
                    sender = tip_info[0]
                    receiver = tip_info[1].split(':')[0]
                    if receiver == self._broker_id:
                        try:
                            self._get_user(sender)
                        except Exception as e:
                            return 'Ottobot thanks you for your generousity, unregistered user'

                        amount = Decimal(tip_info[1].split(':')[1])
                        amount = self._exchange_rate * amount
                        amount = Decimal(amount.quantize(Decimal('.01'), rounding=ROUND_HALF_DOWN))

                        if amount != 0:
                            self._db.broker_give_money_to_user(sender, amount, 'Tipping Ottobot')
                            self._update_single_user(sender)
                            return 'Ottobot winks at you, and walks away whistling. Your pockets feel heavier. (New balance: {})'.format(self._user_cache[sender].balance)
                        else:
                            return 'No foolerino'

            except Exception as e:
                _logger.error('Failed to process tip:({})'.format(message.content))
                _logger.exception(e)
                return 'Failed to process tip({}) Go yell at :otto:'.format(e)<|endoftext|>


<commit_before>from daisyproducer.version import getVersion
from django.conf import settings
from os.path import join, basename, splitext
from shutil import rmtree
from subprocess import call, Popen, PIPE
from lxml import etree
import os
import tempfile

def filterBrlContractionhints(file_path):
    """Filter all the brl:contractionhints from the given file_path.
    This is done using an XSLT stylesheet. Return the name of a
    temporary file that contains the filtered content. The caller is
    responsible for removing the temporary file."""
    tmpFile = tempfile.mkstemp(prefix="daisyproducer-", suffix=".xml")[1]
    command = (
        "xsltproc",
        "--output", tmpFile,
        join(settings.PROJECT_DIR, 'documents', 'xslt', 'filterBrlContractionhints.xsl'),
        file_path,
        )
    call(command)
    return tmpFile

class DaisyPipeline:

    @staticmethod
    def validate(file_path):
        """Validate a given file_path using the Validator from the Daisy
        Pipeline. Return an empty string if the validation was
        successful. Return a list of error messages as delivered by
        the Daisy Pipeline otherwise."""

        relaxng_doc = etree.parse(
            join(settings.PROJECT_DIR, 'documents', 'schema', 'minimalSchema.rng'))
        relaxng = etree.RelaxNG(relaxng_doc)

        doc = etree.parse(file_path)
        if not relaxng.validate(doc):
            entry = relaxng.error_log[0]
            return ["%s on line %s" % (entry.message, entry.line)]

        tmpFile = filterBrlContractionhints(file_path)
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            # FIXME: This requires a version of the pipeline from
            # trunk (>= rev 2480). You could argue that it is
            # pointless to check for a DOCTYPE declaration here as the
            # filterBrlContractionhints above adds it _anyway_. Well
            # let's just leave it in here in case the filter code ever
            # is refactored out.
            join(settings.DAISY_PIPELINE_PATH, 'scripts', 'verify',
                 'ConfigurableValidator.taskScript'),
            "--validatorInputFile=%s" % tmpFile,
            # make sure files with a missing DOCTYPE declaration do
            # not validate.
            "--validatorInputDelegates=%s" %
            "org.daisy.util.fileset.validation.delegate.impl.NoDocTypeDeclarationDelegate",
            )
        result = map(lambda line: line.replace("file:%s" % file_path, "", 1),
                   map(lambda line: line.replace("[ERROR, Validator]", "", 1),
                       filter(lambda line: line.find('[ERROR, Validator]') != -1,
                              Popen(command, stdout=PIPE).communicate()[0].splitlines())))
        os.remove(tmpFile)
        return result

    @staticmethod
    def dtbook2pdf(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to pdf"""
        tmpDir = tempfile.mkdtemp(prefix="daisyproducer-")
        fileBaseName = splitext(basename(inputFile))[0]
        latexFileName = join(tmpDir, fileBaseName + ".tex")

        tmpFile = filterBrlContractionhints(inputFile)
        # Transform to LaTeX using pipeline
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'latex', 'DTBookToLaTeX.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % latexFileName,
            "--fontsize=%(font_size)s" % kwargs,
            "--font=%(font)s" % kwargs,
            "--pageStyle=%(page_style)s" % kwargs,
            "--alignment=%(alignment)s" % kwargs,
            "--papersize=%(paper_size)s" % kwargs,
            )
        call(command)
        os.remove(tmpFile)
        # Transform to pdf using xelatex
        pdfFileName = join(tmpDir, fileBaseName + ".pdf")
        currentDir = os.getcwd()
        os.chdir(tmpDir)
        command = (
            "xelatex",
            "-halt-on-error",
            latexFileName,
            )
        call(command)
        call(command) # call LaTeX again to make sure the toc is inserted
        os.rename(pdfFileName, outputFile)
        os.chdir(currentDir)
        rmtree(tmpDir)

    @staticmethod
    def dtbook2xhtml(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to xhtml"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'xhtml', 'DtbookToXhtml.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2rtf(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to rtf"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'text', 'DtbookToRtf.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2epub(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to EPUB"""
        tmpFile = filterBrlContractionhints(inputFile)
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'epub', 'OPSCreator.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2text_only_fileset(inputFile, outputPath, **kwargs):
        """Transform a dtbook xml file to a Daisy 2.02 Text-Only fileset"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'dtb', 'Fileset-DtbookToDaisy202TextOnly.taskScript'),
            "--input=%s" % tmpFile,
            "--outputPath=%s" % outputPath,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2dtb(inputFile, outputPath, **kwargs):
        """Transform a dtbook xml file to a Daisy Full-Text Full-Audio book"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'dtb', 'Narrator-DtbookToDaisy.taskScript'),
            "--input=%s" % tmpFile,
            "--outputPath=%s" % outputPath,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

class Liblouis:

    contractionMap = {
        0 : 'de-ch-g0.utb',
        1 : 'de-ch-g1.ctb',
        2 : 'de-ch-g2.ctb'}

    yesNoMap = {
        True : "yes",
        False : "no" }

    @staticmethod
    def dtbook2brl(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to brl"""
        tmpFile = filterBrlContractionhints(inputFile)
        # prepare xml input for liblouis
        command = (
            "xsltproc",
            join(settings.PROJECT_DIR, 'documents', 'xslt', 'brailleSanitize.xsl'),
            tmpFile,
            )
        p1 = Popen(command, stdout=PIPE)
        # transform to braille
        command = (
            "xml2brl",
            "-CcellsPerLine=%(cells_per_line)s" % kwargs,
            "-ClinesPerPage=%(lines_per_page)s" % kwargs,
            "-CliteraryTextTable=%s" % Liblouis.contractionMap[kwargs['contraction']],
            "-Chyphenate=%s" % Liblouis.yesNoMap[kwargs['hyphenation']],
            "-CprintPages=%s" % Liblouis.yesNoMap[kwargs['show_original_page_numbers']],
            "-Cinterpoint=no",
            "-CnewEntries=no",
            "-Cinterline=no",
            "-CinputTextEncoding=utf8",
            "-CoutputEncoding=ascii8",
            "-CbraillePageNumberAt=bottom",
            "-CprintPageNumberAt=top",
            "-CbeginningPageNumber=1",
            "-Cparagraphs=yes",
            "-CbraillePages=yes",
            "-CfileEnd=\"^z\"",
            # FIXME: find a solution on how to pass these values
            # r"-CpageEnd=\f",
            # r"-ClineEnd=\n",
            "-CsemanticFiles=dtbook.sem",
            "-CinternetAccess=no",
            )
        f = open(outputFile, 'w')
        p2 = Popen(command, stdin=p1.stdout, stdout=f,
                   # FIXME: xml2brl creates a temporary file in cwd,
                   # so we have to change directory to a place where
                   # www-data can create temporary files
                   cwd=tempfile.gettempdir())
        p2.communicate()
        f.close()
        os.remove(tmpFile)

import louis
import libxml2
import libxslt
import textwrap

class SBSForm:
    wrapper = textwrap.TextWrapper(width=80, initial_indent=' ', subsequent_indent=' ')

    nodeName = None

    modeMap = {
        'plain_text' : louis.plain_text,
        'italic' : louis.italic,
        'underline' : louis.underline,
        'bold' : louis.bold,
        'computer_braille' : louis.computer_braille}

    @staticmethod
    def getTableList(language, grade, options, context):
        tableMap = {("fr", 1) : "fr-fr-g1.utb",
                    ("fr", 2) : "Fr-Fr-g2.ctb",
                    ("fr", 0) : "fr-fr-g1.utb",
                    ("it", 1) : "it-it-g1.utb",
                    ("it", 0) : "it-it-g1.utb",
                    ("en", 1) : "en-us-g1.ctb",
                    ("en", 2) : "en-us-g2.ctb",
                    ("en", 0) : "en-us-g1.ctb"}
        if (language, grade) in tableMap:
            return [tableMap[(language, grade)]]
        elif language == "de":
            if options.find("accents=de-reduced") != -1:
                accentsTable = "sbs-de-accents-reduced.cti"
            elif options.find("accents=de-ch") != -1:
                accentsTable = "sbs-de-accents-ch.cti"
            else:
                accentsTable = "sbs-de-accents.cti"

                return filter(
                    None,
                    ['sbs.dis',
                     'sbs-de-core6.cti',
                     accentsTable if context != "date[month]" and context != "date[day]" else None,
                     'sbs-special.cti',
                     'sbs-whitespace.mod',
                     "sbs-de-capsign.mod" if context == "v-form" or context == "name[capitalized]" or \
                         (grade != 2 and options.find("capitalization=on") != -1 and \
                              (context == "name" or context == "place" or context == "num[ordinal]")) else None,
                     'sbs-de-letsign.mod' if grade == 2 and \
                         context != "date[day]" and context != "date[month]" \
                         and context != "name[capitalized]" else None,
                     'sbs-numsign.mod'if context != "date[month]" else None,
                     'sbs-litdigit-upper.mod' if context != "num[ordinal]" and context != "date[day]" else None,
                     'sbs-litdigit-lower.mod' if context == "num[ordinal]" or context == "date[day]" else None,
                     'sbs-de-core.mod' if context != "date[month]" and context != "date[day]" else None,
                     'sbs-de-g0-core.mod' if context == "name[capitalized]" or context == "abbrev" or \
                         (grade == 0 and context != "date[day]" and context != "date[month]") else None,
                     'sbs-de-g1-core.mod' if grade == 1 and \
                         (context != "name[capitalized]" and context != "abbrev" \
                              and context != "date[month]" and context != "date[day]") else None,
                     'sbs-de-g2-place.mod' if grade == 2 and context == "place" else None,
                     'sbs-de-g2-name.mod' if grade == 2 and (context == "place" or context == "name") else None,
                     'sbs-de-g2-core.mod' if grade == 2 and \
                         (context == None or context == "v-form" or \
                              context == "num[roman]" or context == "num[ordinal]") else None,
                     'sbs-special.mod'
                     ])
        else:
            return ["en-us-g2.ctb"]

    @staticmethod
    def translate(ctx, str, language, grade, mode=None, options=None, context=None):
        global nodeName

        try:
            pctxt = libxslt.xpathParserContext(_obj=ctx)
            ctxt = pctxt.context()
            tctxt = ctxt.transformContext()
            nodeName = tctxt.insertNode().name
        except:
            pass

        typeform = len(str)*[SBSForm.modeMap[mode]] if mode and (mode == "italic" or mode == "bold") else None
        translation_tables = SBSForm.getTableList(language, grade, options, context)
        braille = louis.translate(translation_tables, str.decode('utf-8'), typeform=typeform)[0]
        braille = braille.encode('utf-8')
        return SBSForm.wrapper.fill(braille)

    @staticmethod
    def dtbook2sbsform(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to sbsform"""
        styledoc = libxml2.parseFile(
            join(settings.PROJECT_DIR, 'documents', 'xslt', 'dtbook2sbsform.xsl'))
        style = libxslt.parseStylesheetDoc(styledoc)
        doc = libxml2.parseFile(inputFile)
        kwargs["version"] = getVersion()
        # map True and False to "1" and "0"
        kwargs.update([(k, 1 if v == True else 0) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        # and quote the values
        kwargs.update([(k, "'%s'" % v) for (k, v) in kwargs.iteritems()])
        result = style.applyStylesheet(doc, kwargs)
        style.saveResultToFilename(outputFile, result, 0)
        style.freeStylesheet()
        doc.freeDoc()
        result.freeDoc()

libxslt.registerExtModuleFunction("translate", "http://liblouis.org/liblouis", SBSForm.translate)
<commit_msg>make sure empty options are handled properly<commit_after>from daisyproducer.version import getVersion
from django.conf import settings
from os.path import join, basename, splitext
from shutil import rmtree
from subprocess import call, Popen, PIPE
from lxml import etree
import os
import tempfile

def filterBrlContractionhints(file_path):
    """Filter all the brl:contractionhints from the given file_path.
    This is done using an XSLT stylesheet. Return the name of a
    temporary file that contains the filtered content. The caller is
    responsible for removing the temporary file."""
    tmpFile = tempfile.mkstemp(prefix="daisyproducer-", suffix=".xml")[1]
    command = (
        "xsltproc",
        "--output", tmpFile,
        join(settings.PROJECT_DIR, 'documents', 'xslt', 'filterBrlContractionhints.xsl'),
        file_path,
        )
    call(command)
    return tmpFile

class DaisyPipeline:

    @staticmethod
    def validate(file_path):
        """Validate a given file_path using the Validator from the Daisy
        Pipeline. Return an empty string if the validation was
        successful. Return a list of error messages as delivered by
        the Daisy Pipeline otherwise."""

        relaxng_doc = etree.parse(
            join(settings.PROJECT_DIR, 'documents', 'schema', 'minimalSchema.rng'))
        relaxng = etree.RelaxNG(relaxng_doc)

        doc = etree.parse(file_path)
        if not relaxng.validate(doc):
            entry = relaxng.error_log[0]
            return ["%s on line %s" % (entry.message, entry.line)]

        tmpFile = filterBrlContractionhints(file_path)
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            # FIXME: This requires a version of the pipeline from
            # trunk (>= rev 2480). You could argue that it is
            # pointless to check for a DOCTYPE declaration here as the
            # filterBrlContractionhints above adds it _anyway_. Well
            # let's just leave it in here in case the filter code ever
            # is refactored out.
            join(settings.DAISY_PIPELINE_PATH, 'scripts', 'verify',
                 'ConfigurableValidator.taskScript'),
            "--validatorInputFile=%s" % tmpFile,
            # make sure files with a missing DOCTYPE declaration do
            # not validate.
            "--validatorInputDelegates=%s" %
            "org.daisy.util.fileset.validation.delegate.impl.NoDocTypeDeclarationDelegate",
            )
        result = map(lambda line: line.replace("file:%s" % file_path, "", 1),
                   map(lambda line: line.replace("[ERROR, Validator]", "", 1),
                       filter(lambda line: line.find('[ERROR, Validator]') != -1,
                              Popen(command, stdout=PIPE).communicate()[0].splitlines())))
        os.remove(tmpFile)
        return result

    @staticmethod
    def dtbook2pdf(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to pdf"""
        tmpDir = tempfile.mkdtemp(prefix="daisyproducer-")
        fileBaseName = splitext(basename(inputFile))[0]
        latexFileName = join(tmpDir, fileBaseName + ".tex")

        tmpFile = filterBrlContractionhints(inputFile)
        # Transform to LaTeX using pipeline
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'latex', 'DTBookToLaTeX.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % latexFileName,
            "--fontsize=%(font_size)s" % kwargs,
            "--font=%(font)s" % kwargs,
            "--pageStyle=%(page_style)s" % kwargs,
            "--alignment=%(alignment)s" % kwargs,
            "--papersize=%(paper_size)s" % kwargs,
            )
        call(command)
        os.remove(tmpFile)
        # Transform to pdf using xelatex
        pdfFileName = join(tmpDir, fileBaseName + ".pdf")
        currentDir = os.getcwd()
        os.chdir(tmpDir)
        command = (
            "xelatex",
            "-halt-on-error",
            latexFileName,
            )
        call(command)
        call(command) # call LaTeX again to make sure the toc is inserted
        os.rename(pdfFileName, outputFile)
        os.chdir(currentDir)
        rmtree(tmpDir)

    @staticmethod
    def dtbook2xhtml(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to xhtml"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'xhtml', 'DtbookToXhtml.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2rtf(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to rtf"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'text', 'DtbookToRtf.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2epub(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to EPUB"""
        tmpFile = filterBrlContractionhints(inputFile)
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'epub', 'OPSCreator.taskScript'),
            "--input=%s" % tmpFile,
            "--output=%s" % outputFile,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2text_only_fileset(inputFile, outputPath, **kwargs):
        """Transform a dtbook xml file to a Daisy 2.02 Text-Only fileset"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'dtb', 'Fileset-DtbookToDaisy202TextOnly.taskScript'),
            "--input=%s" % tmpFile,
            "--outputPath=%s" % outputPath,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

    @staticmethod
    def dtbook2dtb(inputFile, outputPath, **kwargs):
        """Transform a dtbook xml file to a Daisy Full-Text Full-Audio book"""
        tmpFile = filterBrlContractionhints(inputFile)
        # map True and False to "true" and "false"
        kwargs.update([(k, str(v).lower()) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        command = (
            join(settings.DAISY_PIPELINE_PATH, 'pipeline.sh'),
            join(settings.DAISY_PIPELINE_PATH, 'scripts',
                 'create_distribute', 'dtb', 'Narrator-DtbookToDaisy.taskScript'),
            "--input=%s" % tmpFile,
            "--outputPath=%s" % outputPath,
            )
        for k, v in kwargs.iteritems():
            command += ("--%s=%s" % (k,v),)
        call(command)
        os.remove(tmpFile)

class Liblouis:

    contractionMap = {
        0 : 'de-ch-g0.utb',
        1 : 'de-ch-g1.ctb',
        2 : 'de-ch-g2.ctb'}

    yesNoMap = {
        True : "yes",
        False : "no" }

    @staticmethod
    def dtbook2brl(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to brl"""
        tmpFile = filterBrlContractionhints(inputFile)
        # prepare xml input for liblouis
        command = (
            "xsltproc",
            join(settings.PROJECT_DIR, 'documents', 'xslt', 'brailleSanitize.xsl'),
            tmpFile,
            )
        p1 = Popen(command, stdout=PIPE)
        # transform to braille
        command = (
            "xml2brl",
            "-CcellsPerLine=%(cells_per_line)s" % kwargs,
            "-ClinesPerPage=%(lines_per_page)s" % kwargs,
            "-CliteraryTextTable=%s" % Liblouis.contractionMap[kwargs['contraction']],
            "-Chyphenate=%s" % Liblouis.yesNoMap[kwargs['hyphenation']],
            "-CprintPages=%s" % Liblouis.yesNoMap[kwargs['show_original_page_numbers']],
            "-Cinterpoint=no",
            "-CnewEntries=no",
            "-Cinterline=no",
            "-CinputTextEncoding=utf8",
            "-CoutputEncoding=ascii8",
            "-CbraillePageNumberAt=bottom",
            "-CprintPageNumberAt=top",
            "-CbeginningPageNumber=1",
            "-Cparagraphs=yes",
            "-CbraillePages=yes",
            "-CfileEnd=\"^z\"",
            # FIXME: find a solution on how to pass these values
            # r"-CpageEnd=\f",
            # r"-ClineEnd=\n",
            "-CsemanticFiles=dtbook.sem",
            "-CinternetAccess=no",
            )
        f = open(outputFile, 'w')
        p2 = Popen(command, stdin=p1.stdout, stdout=f,
                   # FIXME: xml2brl creates a temporary file in cwd,
                   # so we have to change directory to a place where
                   # www-data can create temporary files
                   cwd=tempfile.gettempdir())
        p2.communicate()
        f.close()
        os.remove(tmpFile)

import louis
import libxml2
import libxslt
import textwrap

class SBSForm:
    wrapper = textwrap.TextWrapper(width=80, initial_indent=' ', subsequent_indent=' ')

    nodeName = None

    modeMap = {
        'plain_text' : louis.plain_text,
        'italic' : louis.italic,
        'underline' : louis.underline,
        'bold' : louis.bold,
        'computer_braille' : louis.computer_braille}

    @staticmethod
    def getTableList(language, grade, options, context):
        if options == None:
            options = ""
        tableMap = {("fr", 1) : "fr-fr-g1.utb",
                    ("fr", 2) : "Fr-Fr-g2.ctb",
                    ("fr", 0) : "fr-fr-g1.utb",
                    ("it", 1) : "it-it-g1.utb",
                    ("it", 0) : "it-it-g1.utb",
                    ("en", 1) : "en-us-g1.ctb",
                    ("en", 2) : "en-us-g2.ctb",
                    ("en", 0) : "en-us-g1.ctb"}
        if (language, grade) in tableMap:
            return [tableMap[(language, grade)]]
        elif language == "de":
            if options.find("accents=de-reduced") != -1:
                accentsTable = "sbs-de-accents-reduced.cti"
            elif options.find("accents=de-ch") != -1:
                accentsTable = "sbs-de-accents-ch.cti"
            else:
                accentsTable = "sbs-de-accents.cti"

                return filter(
                    None,
                    ['sbs.dis',
                     'sbs-de-core6.cti',
                     accentsTable if context != "date[month]" and context != "date[day]" else None,
                     'sbs-special.cti',
                     'sbs-whitespace.mod',
                     "sbs-de-capsign.mod" if context == "v-form" or context == "name[capitalized]" or \
                         (grade != 2 and options.find("capitalization=on") != -1 and \
                              (context == "name" or context == "place" or context == "num[ordinal]")) else None,
                     'sbs-de-letsign.mod' if grade == 2 and \
                         context != "date[day]" and context != "date[month]" \
                         and context != "name[capitalized]" else None,
                     'sbs-numsign.mod'if context != "date[month]" else None,
                     'sbs-litdigit-upper.mod' if context != "num[ordinal]" and context != "date[day]" else None,
                     'sbs-litdigit-lower.mod' if context == "num[ordinal]" or context == "date[day]" else None,
                     'sbs-de-core.mod' if context != "date[month]" and context != "date[day]" else None,
                     'sbs-de-g0-core.mod' if context == "name[capitalized]" or context == "abbrev" or \
                         (grade == 0 and context != "date[day]" and context != "date[month]") else None,
                     'sbs-de-g1-core.mod' if grade == 1 and \
                         (context != "name[capitalized]" and context != "abbrev" \
                              and context != "date[month]" and context != "date[day]") else None,
                     'sbs-de-g2-place.mod' if grade == 2 and context == "place" else None,
                     'sbs-de-g2-name.mod' if grade == 2 and (context == "place" or context == "name") else None,
                     'sbs-de-g2-core.mod' if grade == 2 and \
                         (context == None or context == "v-form" or \
                              context == "num[roman]" or context == "num[ordinal]") else None,
                     'sbs-special.mod'
                     ])
        else:
            return ["en-us-g2.ctb"]

    @staticmethod
    def translate(ctx, str, language, grade, mode=None, options=None, context=None):
        global nodeName

        try:
            pctxt = libxslt.xpathParserContext(_obj=ctx)
            ctxt = pctxt.context()
            tctxt = ctxt.transformContext()
            nodeName = tctxt.insertNode().name
        except:
            pass

        typeform = len(str)*[SBSForm.modeMap[mode]] if mode and (mode == "italic" or mode == "bold") else None
        translation_tables = SBSForm.getTableList(language, int(grade), options, context)
        braille = louis.translate(translation_tables, str.decode('utf-8'), typeform=typeform)[0]
        braille = braille.encode('utf-8')
        return SBSForm.wrapper.fill(braille)

    @staticmethod
    def dtbook2sbsform(inputFile, outputFile, **kwargs):
        """Transform a dtbook xml file to sbsform"""
        styledoc = libxml2.parseFile(
            join(settings.PROJECT_DIR, 'documents', 'xslt', 'dtbook2sbsform.xsl'))
        style = libxslt.parseStylesheetDoc(styledoc)
        doc = libxml2.parseFile(inputFile)
        kwargs["version"] = getVersion()
        # map True and False to "1" and "0"
        kwargs.update([(k, 1 if v == True else 0) for (k, v) in kwargs.iteritems() if isinstance(v, bool)])
        # and quote the values
        kwargs.update([(k, "'%s'" % v) for (k, v) in kwargs.iteritems()])
        result = style.applyStylesheet(doc, kwargs)
        style.saveResultToFilename(outputFile, result, 0)
        style.freeStylesheet()
        doc.freeDoc()
        result.freeDoc()

libxslt.registerExtModuleFunction("translate", "http://liblouis.org/liblouis", SBSForm.translate)
<|endoftext|>