<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import os.path
import subprocess

from . import options, util
from .repo import Repo

nginx_ssl_config = """
{conf}
server {{
    listen 80 {extra_listen_options};
    listen [::]:80 {extra_listen_options};
    server_name {domain};
    rewrite ^ https://$http_host$request_uri? permanent;
}}
server {{
    listen 443 {extra_listen_options};
    listen [::]:443 {extra_listen_options};
    server_name {domain};
    ssl_certificate /etc/ssl/private/httpd/{domain}/{domain}.crt;
    ssl_certificate_key /etc/ssl/private/httpd/{domain}/{domain}.key;
    ssl_session_timeout 5m;
    ssl_dhparam /etc/ssl/private/httpd/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security max-age=15768000;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/private/httpd/{domain}/trusted_chain.crt;
    resolver 8.8.8.8 8.8.4.4;
    {locations}
}}
"""  # nopep8 (silence pep8 warning about long lines)

nginx_config = """
{conf}
server {{
    listen 80 {extra_listen_options};
    listen [::]:80 {extra_listen_options};
    server_name {domain};
    {locations}
}}
"""  # nopep8 (silence pep8 warning about long lines)

location = """
location {path} {{
    {contents}
}}
"""

class Domain(object):
    def __init__(self, name):
        self.name = name
        try:
            self.config = options.domains[name]
        except KeyError:
            raise ValueError('This domain is not configured')

    @classmethod
    def all(cls):
        for key in options.domains:
            yield cls(key)

    @classmethod
    def configure_all(cls):
        for d in cls.all():
            d.configure(nginx_reload=False)
        cls.nginx_reload()

    @classmethod
    def nginx_configtest(cls):
        try:
            subprocess.check_call(['sudo', '/usr/sbin/nginx', '-t'])
        except subprocess.CalledProcessError:
            return False
        return True

    @classmethod
    def nginx_reload(cls):
        subprocess.check_call(['sudo', '/usr/sbin/nginx', '-s', 'reload'])

    @property
    def nginx_config_path(self):
        return os.path.join(
            os.path.expanduser('~/nginx.d/'),
            self.name + ".conf"
        )

    @property
    def runners(self):
        res = dict()
        for path, config in self.config['locations'].items():
            c = config['upstream']
            try:
                branch = Repo(c['repo']).branches[c['branch']]
                runner = branch.runners[c['runner']]
                if (runner.in_maintenance or
                        branch.current_checkout is None) and \
                        'maintenance_upstream' in config:
                    c = config['maintenance_upstream']
                    branch = Repo(c['repo']).branches[c['branch']]
                    runner = branch.runners[c['runner']]
                if branch.current_checkout is not None:
                    res[path] = runner
            except KeyError:
                raise ValueError('Repo, branch or runner not found for {}'
                                 .format(repr(c)))
        return res

    def configure(self, nginx_reload=True):
        util.mkdir_p(os.path.expanduser('~/nginx.d/'))
        try:
            # Remove old broken config
            os.unlink(self.nginx_config_path + '.broken')
        except FileNotFoundError:
            pass

        args = dict(
            domain=self.name,
            extra_listen_options=self.config.get(
                'extra_listen_options', ''
            ),
            conf='\n'.join(
                runner.nginx_conf for runner in self.runners.values()
            ),
            locations='\n'.join(
                location.format(
                    path=path,
                    contents=runner.nginx_location
                ) for path, runner in self.runners.items()
            )
        )
        if self.config.get('ssl', True):
            util.replace_file(
                self.nginx_config_path,
                nginx_ssl_config.format(**args)
            )
        else:
            util.replace_file(
                self.nginx_config_path,
                nginx_config.format(**args)
            )
        if not self.nginx_configtest():
            # That file is probably broken => rename it
            os.rename(
                self.nginx_config_path,
                self.nginx_config_path + '.broken'
            )
            raise RuntimeError('nginx configuration failed')
        if nginx_reload:
            self.nginx_reload()

    @classmethod
    def cleanup(cls):
        config_files = set([d.nginx_config_path for d in cls.all()])
        to_remove = []
        for f in os.listdir((os.path.expanduser('~/nginx.d'))):
            f = os.path.join(os.path.expanduser('~/nginx.d'), f)
            if f not in config_files:
                to_remove.append(f)
        [os.unlink(f) for f in to_remove]
<commit_msg>Add ssl option to nginx config<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import os.path
import subprocess

from . import options, util
from .repo import Repo

nginx_ssl_config = """
{conf}
server {{
    listen 80 {extra_listen_options};
    listen [::]:80 {extra_listen_options};
    server_name {domain};
    rewrite ^ https://$http_host$request_uri? permanent;
}}
server {{
    listen 443 ssl {extra_listen_options};
    listen [::]:443 ssl {extra_listen_options};
    server_name {domain};
    ssl_certificate /etc/ssl/private/httpd/{domain}/{domain}.crt;
    ssl_certificate_key /etc/ssl/private/httpd/{domain}/{domain}.key;
    ssl_session_timeout 5m;
    ssl_dhparam /etc/ssl/private/httpd/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security max-age=15768000;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/private/httpd/{domain}/trusted_chain.crt;
    resolver 8.8.8.8 8.8.4.4;
    {locations}
}}
"""  # nopep8 (silence pep8 warning about long lines)

nginx_config = """
{conf}
server {{
    listen 80 {extra_listen_options};
    listen [::]:80 {extra_listen_options};
    server_name {domain};
    {locations}
}}
"""  # nopep8 (silence pep8 warning about long lines)

location = """
location {path} {{
    {contents}
}}
"""

class Domain(object):
    def __init__(self, name):
        self.name = name
        try:
            self.config = options.domains[name]
        except KeyError:
            raise ValueError('This domain is not configured')

    @classmethod
    def all(cls):
        for key in options.domains:
            yield cls(key)

    @classmethod
    def configure_all(cls):
        for d in cls.all():
            d.configure(nginx_reload=False)
        cls.nginx_reload()

    @classmethod
    def nginx_configtest(cls):
        try:
            subprocess.check_call(['sudo', '/usr/sbin/nginx', '-t'])
        except subprocess.CalledProcessError:
            return False
        return True

    @classmethod
    def nginx_reload(cls):
        subprocess.check_call(['sudo', '/usr/sbin/nginx', '-s', 'reload'])

    @property
    def nginx_config_path(self):
        return os.path.join(
            os.path.expanduser('~/nginx.d/'),
            self.name + ".conf"
        )

    @property
    def runners(self):
        res = dict()
        for path, config in self.config['locations'].items():
            c = config['upstream']
            try:
                branch = Repo(c['repo']).branches[c['branch']]
                runner = branch.runners[c['runner']]
                if (runner.in_maintenance or
                        branch.current_checkout is None) and \
                        'maintenance_upstream' in config:
                    c = config['maintenance_upstream']
                    branch = Repo(c['repo']).branches[c['branch']]
                    runner = branch.runners[c['runner']]
                if branch.current_checkout is not None:
                    res[path] = runner
            except KeyError:
                raise ValueError('Repo, branch or runner not found for {}'
                                 .format(repr(c)))
        return res

    def configure(self, nginx_reload=True):
        util.mkdir_p(os.path.expanduser('~/nginx.d/'))
        try:
            # Remove old broken config
            os.unlink(self.nginx_config_path + '.broken')
        except FileNotFoundError:
            pass

        args = dict(
            domain=self.name,
            extra_listen_options=self.config.get(
                'extra_listen_options', ''
            ),
            conf='\n'.join(
                runner.nginx_conf for runner in self.runners.values()
            ),
            locations='\n'.join(
                location.format(
                    path=path,
                    contents=runner.nginx_location
                ) for path, runner in self.runners.items()
            )
        )
        if self.config.get('ssl', True):
            util.replace_file(
                self.nginx_config_path,
                nginx_ssl_config.format(**args)
            )
        else:
            util.replace_file(
                self.nginx_config_path,
                nginx_config.format(**args)
            )
        if not self.nginx_configtest():
            # That file is probably broken => rename it
            os.rename(
                self.nginx_config_path,
                self.nginx_config_path + '.broken'
            )
            raise RuntimeError('nginx configuration failed')
        if nginx_reload:
            self.nginx_reload()

    @classmethod
    def cleanup(cls):
        config_files = set([d.nginx_config_path for d in cls.all()])
        to_remove = []
        for f in os.listdir((os.path.expanduser('~/nginx.d'))):
            f = os.path.join(os.path.expanduser('~/nginx.d'), f)
            if f not in config_files:
                to_remove.append(f)
        [os.unlink(f) for f in to_remove]
<|endoftext|>


<commit_before>import json
from collections import deque
from datetime import datetime
from functools import lru_cache

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import shortest_path

from .position import Position
from .route import Route

class Router():
    default_settings = {
        'steps': 'yes',
        'stairs': 'yes',
        'escalators': 'yes',
        'elevators': 'yes',
        'route': 'fast',
        'e': (),
        'i': (),
        's-default': 160,
        's-elevator': 20,
        's-stairs-up': 130,
        's-stairs-down': 160,
        's-escalator-up': 160,
        's-escalator-down': 160,
    }

    def __init__(self, graph, settings={}):
        self.graph = graph
        self.settings = self.default_settings.copy()
        self.settings.update(graph.overwrite_default_settings)
        self.update_settings(settings)

    def update_settings(self, settings):
        for name in ('steps', 'stairs', 'escalators', 'elevators'):
            if settings.get(name) in ('yes', 'up', 'down', 'no'):
                self.settings[name] = settings[name]

        if settings.get('elevator', '').isdigit() and int(settings.get('elevator')) in range(0, 900):
            self.settings['elevator'] = int(settings.get('elevator'))

        for name in ('s-walking', 's-stairs-up', 's-stairs-down', 's-escalator-up', 's-escalator-down'):
            if name not in settings or not str(settings[name]).isdigit():
                continue

            if int(settings[name]) in range(1, 30000):
                self.settings[name] = int(settings[name])

        if settings.get('route') in ('fast', 'short'):
            self.settings['route'] = settings['route']

        if 'e' in settings:
            self.settings['e'] = set(e for e in tuple(self.settings['e'])+tuple(settings['e'])
                                     if e in self.graph.selectable_locations)

        if 'i' in settings:
            self.settings['e'] = set(self.settings['e']) - set(settings['i'])
            self.settings['i'] = set(i for i in tuple(self.settings['i'])+tuple(settings['i'])
                                     if i in self.graph.selectable_locations)

        self.settings['e'] = tuple(self.settings['e'])
        self.settings['i'] = tuple(self.settings['i'])

    def create_routing_table(self):
        self.excluded_nodes, g_dense = Router.create_dense_matrix(self.graph, tuple(self.settings.items()))
        self.shortest_paths, self.predecessors = Router.shortest_path(g_dense.tostring(), g_dense.shape)

    @classmethod
    def get_factors_by_settings(cls, settings):
        return {
            'default': 1/settings['s-default'],
            'steps-up': 1/settings['s-default'],
            'steps-down': 1/settings['s-default'],
            'stairs-up': 1/settings['s-stairs-up'],
            'stairs-down': 1/settings['s-stairs-down'],
            'escalator-up': 1/settings['s-escalator-up'],
            'escalator-down': 1/settings['s-escalator-down'],
            'elevator-up': settings['s-elevator'],
            'elevator-down': settings['s-elevator']
        }

    @classmethod
    @lru_cache(maxsize=128)
    def create_dense_matrix(cls, graph, settings):
        # First, we select all routing matrices of the allowed connection types,
        # multiply them with their speed and combine them
        settings = dict(settings)
        g_dense = np.zeros(graph.matrices['default'].shape)

        factors = {}
        if settings['route'] == 'fast':
            factors = cls.get_factors_by_settings(settings)
        else:
            factors = {
                'default': 1, 'steps-up': 1, 'steps-down': 1, 'stairs-up': 1, 'stairs-down': 1, 'escalator-up': 1,
                'escalator-down': 1, 'elevator-up': 1, 'elevator-down': 1
            }

        if settings['steps'] not in ('yes', 'up'):
            factors['steps-up'] *= 10000000

        if settings['steps'] not in ('yes', 'down'):
            factors['steps-down'] *= 10000000

        if settings['stairs'] not in ('yes', 'up'):
            factors['stairs-up'] *= 10000000

        if settings['stairs'] not in ('yes', 'down'):
            factors['stairs-down'] *= 10000000

        if settings['escalators'] not in ('yes', 'up'):
            factors['escalator-up'] *= 10000000

        if settings['escalators'] not in ('yes', 'down'):
            factors['escalator-down'] *= 10000000

        if settings['elevators'] not in ('yes', 'up'):
            factors['elevator-up'] *= 10000000

        if settings['elevators'] not in ('yes', 'down'):
            factors['elevator-down'] *= 10000000

        for name, factor in factors.items():
            g_dense += graph.matrices[name]*factor

        # Its time to find out which nodes exactly were excluded from routing
        excluded = set(sum(([p.i for p in graph.selectable_locations[e].nodes]
                            for e in settings['e']), []))

        # And now lets exclude the nodes from exclusion that were explicitely included
        excluded -= set(sum(([p.i for p in graph.selectable_locations[i].nodes]
                            for i in settings['i']), []))

        # We know now what should be excluded, so let's make this connection almost impossible
        ex = tuple(excluded)
        g_dense[ex, :] *= 10000000
        g_dense[:, ex] *= 10000000

        return excluded, g_dense

    @classmethod
    @lru_cache(maxsize=128)
    def shortest_path(cls, data, shape):
        # let scipy do it's magic and calculate all shortest paths in the remaining graph
        g_sparse = csr_matrix(np.ma.masked_values(np.fromstring(data).reshape(shape), 0))
        return shortest_path(g_sparse, return_predecessors=True)

    def get_route(self, origin, destination):
        print(datetime.now(), origin, destination, json.dumps(self.settings))
        messages = []

        if isinstance(origin, Position) and not hasattr(origin, 'nodes'):
            self.graph.connect_position(origin)

        if isinstance(destination, Position) and not hasattr(destination, 'nodes'):
            self.graph.connect_position(destination)

        origin_nodes = set(p.i for p in origin.nodes)
        destination_nodes = set(p.i for p in destination.nodes)

        self.create_routing_table()

        if not (origin_nodes - self.excluded_nodes):
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because your origin has no point outside of them.'))

        if not (destination_nodes - self.excluded_nodes):
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because your destination has no point outside of them.'))

        if origin_nodes & destination_nodes:
            if not isinstance(origin, Position) and not isinstance(destination, Position):
                messages.append(('success', 'Congratulations – you are already there!'))
                return messages, None

            if origin.level == destination.level and origin.x == destination.x and origin.y == destination.y:
                messages.append(('success', 'Congratulations – you are already there!'))
                return messages, None

            if self.graph.can_connect_positions(origin, destination):
                return messages, Route(self.graph, [origin, destination], self.settings, [])

            via = None
            via_dist = float('inf')
            for i in (origin_nodes & destination_nodes):
                distance = (self.graph.get_connection(origin, self.graph.nodes[i])[1] +
                            self.graph.get_connection(origin, self.graph.nodes[i])[1])
                if distance < via_dist:
                    via = i
                    via_dist = distance

            return messages, Route(self.graph, [origin, self.graph.nodes[via], destination], self.settings, [])

        # Remove all routes that dont have the correct origin or destination
        # If we have free placed points, add the delay to their nodes
        possible_routes = self.shortest_paths.copy()
        wayfactor = (1/self.settings['s-default']) if self.settings['route'] == 'fast' else 1
        for i in range(len(self.graph.nodes)):
            if i not in origin_nodes:
                possible_routes[i, :] = np.inf
            elif isinstance(origin, Position):
                possible_routes[i, :] += origin.node_distances[i]*wayfactor

            if i not in destination_nodes:
                possible_routes[:, i] = np.inf
            elif isinstance(destination, Position):
                possible_routes[i, :] += destination.node_distances[i]*wayfactor

        # We may still have multiple routes if we had multiple origin or destination nodes
        # So let's pick the best route
        best_route = tuple(np.transpose(np.where(possible_routes == possible_routes.min()))[0])

        # Collect all route nodes
        firstnode, prevnode = best_route
        route = deque()
        while prevnode >= 0:
            route.appendleft(prevnode)
            prevnode = self.predecessors[firstnode, prevnode]

        if (set(route) - origin_nodes - destination_nodes) & self.excluded_nodes:
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because otherwise no route would be possible.'))

        positions = [self.graph.nodes[i] for i in route]

        if isinstance(origin, Position):
            positions.insert(0, origin)

        if isinstance(destination, Position):
            positions.append(destination)

        # did we use unallowed ctypes?
        avoided_ctypes = set()
        if self.settings['steps'] not in ('yes', 'up'):
            avoided_ctypes.add('steps-up')
        if self.settings['steps'] not in ('yes', 'down'):
            avoided_ctypes.add('steps-down')
        if self.settings['stairs'] not in ('yes', 'up'):
            avoided_ctypes.add('stairs-up')
        if self.settings['stairs'] not in ('yes', 'down'):
            avoided_ctypes.add('stairs-down')
        if self.settings['escalators'] not in ('yes', 'up'):
            avoided_ctypes.add('escalator-up')
        if self.settings['escalators'] not in ('yes', 'down'):
            avoided_ctypes.add('escalator-down')
        if self.settings['elevators'] not in ('yes', 'up'):
            avoided_ctypes.add('elevator-up')
        if self.settings['elevators'] not in ('yes', 'down'):
            avoided_ctypes.add('elevator-down')

        return messages, Route(self.graph, positions, self.settings, avoided_ctypes)
<commit_msg>avoided_ctypes into own method<commit_after>import json
from collections import deque
from datetime import datetime
from functools import lru_cache

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import shortest_path

from .position import Position
from .route import Route

class Router():
    default_settings = {
        'steps': 'yes',
        'stairs': 'yes',
        'escalators': 'yes',
        'elevators': 'yes',
        'route': 'fast',
        'e': (),
        'i': (),
        's-default': 160,
        's-elevator': 20,
        's-stairs-up': 130,
        's-stairs-down': 160,
        's-escalator-up': 160,
        's-escalator-down': 160,
    }

    def __init__(self, graph, settings={}):
        self.graph = graph
        self.settings = self.default_settings.copy()
        self.settings.update(graph.overwrite_default_settings)
        self.update_settings(settings)

    def update_settings(self, settings):
        for name in ('steps', 'stairs', 'escalators', 'elevators'):
            if settings.get(name) in ('yes', 'up', 'down', 'no'):
                self.settings[name] = settings[name]

        if settings.get('elevator', '').isdigit() and int(settings.get('elevator')) in range(0, 900):
            self.settings['elevator'] = int(settings.get('elevator'))

        for name in ('s-walking', 's-stairs-up', 's-stairs-down', 's-escalator-up', 's-escalator-down'):
            if name not in settings or not str(settings[name]).isdigit():
                continue

            if int(settings[name]) in range(1, 30000):
                self.settings[name] = int(settings[name])

        if settings.get('route') in ('fast', 'short'):
            self.settings['route'] = settings['route']

        if 'e' in settings:
            self.settings['e'] = set(e for e in tuple(self.settings['e'])+tuple(settings['e'])
                                     if e in self.graph.selectable_locations)

        if 'i' in settings:
            self.settings['e'] = set(self.settings['e']) - set(settings['i'])
            self.settings['i'] = set(i for i in tuple(self.settings['i'])+tuple(settings['i'])
                                     if i in self.graph.selectable_locations)

        self.settings['e'] = tuple(self.settings['e'])
        self.settings['i'] = tuple(self.settings['i'])

    def create_routing_table(self):
        self.excluded_nodes, g_dense = Router.create_dense_matrix(self.graph, tuple(self.settings.items()))
        self.shortest_paths, self.predecessors = Router.shortest_path(g_dense.tostring(), g_dense.shape)

    @classmethod
    def get_factors_by_settings(cls, settings):
        return {
            'default': 1/settings['s-default'],
            'steps-up': 1/settings['s-default'],
            'steps-down': 1/settings['s-default'],
            'stairs-up': 1/settings['s-stairs-up'],
            'stairs-down': 1/settings['s-stairs-down'],
            'escalator-up': 1/settings['s-escalator-up'],
            'escalator-down': 1/settings['s-escalator-down'],
            'elevator-up': settings['s-elevator'],
            'elevator-down': settings['s-elevator']
        }

    @classmethod
    @lru_cache(maxsize=128)
    def create_dense_matrix(cls, graph, settings):
        # First, we select all routing matrices of the allowed connection types,
        # multiply them with their speed and combine them
        settings = dict(settings)
        g_dense = np.zeros(graph.matrices['default'].shape)

        factors = {}
        if settings['route'] == 'fast':
            factors = cls.get_factors_by_settings(settings)
        else:
            factors = {
                'default': 1, 'steps-up': 1, 'steps-down': 1, 'stairs-up': 1, 'stairs-down': 1, 'escalator-up': 1,
                'escalator-down': 1, 'elevator-up': 1, 'elevator-down': 1
            }

        if settings['steps'] not in ('yes', 'up'):
            factors['steps-up'] *= 10000000

        if settings['steps'] not in ('yes', 'down'):
            factors['steps-down'] *= 10000000

        if settings['stairs'] not in ('yes', 'up'):
            factors['stairs-up'] *= 10000000

        if settings['stairs'] not in ('yes', 'down'):
            factors['stairs-down'] *= 10000000

        if settings['escalators'] not in ('yes', 'up'):
            factors['escalator-up'] *= 10000000

        if settings['escalators'] not in ('yes', 'down'):
            factors['escalator-down'] *= 10000000

        if settings['elevators'] not in ('yes', 'up'):
            factors['elevator-up'] *= 10000000

        if settings['elevators'] not in ('yes', 'down'):
            factors['elevator-down'] *= 10000000

        for name, factor in factors.items():
            g_dense += graph.matrices[name]*factor

        # Its time to find out which nodes exactly were excluded from routing
        excluded = set(sum(([p.i for p in graph.selectable_locations[e].nodes]
                            for e in settings['e']), []))

        # And now lets exclude the nodes from exclusion that were explicitely included
        excluded -= set(sum(([p.i for p in graph.selectable_locations[i].nodes]
                            for i in settings['i']), []))

        # We know now what should be excluded, so let's make this connection almost impossible
        ex = tuple(excluded)
        g_dense[ex, :] *= 10000000
        g_dense[:, ex] *= 10000000

        return excluded, g_dense

    @classmethod
    @lru_cache(maxsize=128)
    def shortest_path(cls, data, shape):
        # let scipy do it's magic and calculate all shortest paths in the remaining graph
        g_sparse = csr_matrix(np.ma.masked_values(np.fromstring(data).reshape(shape), 0))
        return shortest_path(g_sparse, return_predecessors=True)

    def avoided_ctypes(self):
        avoided_ctypes = set()
        if self.settings['steps'] not in ('yes', 'up'):
            avoided_ctypes.add('steps-up')
        if self.settings['steps'] not in ('yes', 'down'):
            avoided_ctypes.add('steps-down')
        if self.settings['stairs'] not in ('yes', 'up'):
            avoided_ctypes.add('stairs-up')
        if self.settings['stairs'] not in ('yes', 'down'):
            avoided_ctypes.add('stairs-down')
        if self.settings['escalators'] not in ('yes', 'up'):
            avoided_ctypes.add('escalator-up')
        if self.settings['escalators'] not in ('yes', 'down'):
            avoided_ctypes.add('escalator-down')
        if self.settings['elevators'] not in ('yes', 'up'):
            avoided_ctypes.add('elevator-up')
        if self.settings['elevators'] not in ('yes', 'down'):
            avoided_ctypes.add('elevator-down')

        return avoided_ctypes

    def get_route(self, origin, destination):
        print(datetime.now(), origin, destination, json.dumps(self.settings))
        messages = []

        if isinstance(origin, Position) and not hasattr(origin, 'nodes'):
            self.graph.connect_position(origin)

        if isinstance(destination, Position) and not hasattr(destination, 'nodes'):
            self.graph.connect_position(destination)

        origin_nodes = set(p.i for p in origin.nodes)
        destination_nodes = set(p.i for p in destination.nodes)

        self.create_routing_table()

        if not (origin_nodes - self.excluded_nodes):
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because your origin has no point outside of them.'))

        if not (destination_nodes - self.excluded_nodes):
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because your destination has no point outside of them.'))

        if origin_nodes & destination_nodes:
            if not isinstance(origin, Position) and not isinstance(destination, Position):
                messages.append(('success', 'Congratulations – you are already there!'))
                return messages, None

            if origin.level == destination.level and origin.x == destination.x and origin.y == destination.y:
                messages.append(('success', 'Congratulations – you are already there!'))
                return messages, None

            if self.graph.can_connect_positions(origin, destination):
                return messages, Route(self.graph, [origin, destination], self.settings, [])

            via = None
            via_dist = float('inf')
            for i in (origin_nodes & destination_nodes):
                distance = (self.graph.get_connection(origin, self.graph.nodes[i])[1] +
                            self.graph.get_connection(origin, self.graph.nodes[i])[1])
                if distance < via_dist:
                    via = i
                    via_dist = distance

            return messages, Route(self.graph, [origin, self.graph.nodes[via], destination], self.settings, [])

        # Remove all routes that dont have the correct origin or destination
        # If we have free placed points, add the delay to their nodes
        possible_routes = self.shortest_paths.copy()
        wayfactor = (1/self.settings['s-default']) if self.settings['route'] == 'fast' else 1
        for i in range(len(self.graph.nodes)):
            if i not in origin_nodes:
                possible_routes[i, :] = np.inf
            elif isinstance(origin, Position):
                possible_routes[i, :] += origin.node_distances[i]*wayfactor

            if i not in destination_nodes:
                possible_routes[:, i] = np.inf
            elif isinstance(destination, Position):
                possible_routes[i, :] += destination.node_distances[i]*wayfactor

        # We may still have multiple routes if we had multiple origin or destination nodes
        # So let's pick the best route
        best_route = tuple(np.transpose(np.where(possible_routes == possible_routes.min()))[0])

        # Collect all route nodes
        firstnode, prevnode = best_route
        route = deque()
        while prevnode >= 0:
            route.appendleft(prevnode)
            prevnode = self.predecessors[firstnode, prevnode]

        if (set(route) - origin_nodes - destination_nodes) & self.excluded_nodes:
            messages.append(('warn', 'This route contains locations that you wanted to avoid '
                                     'because otherwise no route would be possible.'))

        positions = [self.graph.nodes[i] for i in route]

        if isinstance(origin, Position):
            positions.insert(0, origin)

        if isinstance(destination, Position):
            positions.append(destination)

        avoided_ctypes = self.avoided_ctypes()

        return messages, Route(self.graph, positions, self.settings, avoided_ctypes)
<|endoftext|>


<commit_before>#!/usr/bin/python

import unittest
import mock
import dotfilesinstaller
import platform
import sys
import os
import io
import time

class DotfilesTest(unittest.TestCase):

  def setUp(self):
    dotfilesinstaller.init()
    dotfilesinstaller.identifySystem()
    dotfilesinstaller.cleanUp()
    self.symlinkTarget = 'bar'
    self.macBashOutputFile = dotfilesinstaller.macBashOutputFile
    self.macBashOutputDotFile = '.' + self.macBashOutputFile
    self.linuxBashOutputFile = dotfilesinstaller.linuxBashOutputFile
    self.linuxBashOutputDotFile = '.' + self.linuxBashOutputFile

  def tearDown(self):
    self.createdSymlink = dotfilesinstaller.homeDir + 'foo'
    if os.path.islink(self.createdSymlink):
      os.remove(self.createdSymlink)
    if os.path.isfile(self.symlinkTarget):
      os.remove(self.symlinkTarget)
    dotfilesinstaller.cleanUp()

  @mock.patch('platform.system', mock.MagicMock(return_value='Darwin'))
  def testWhenSystemIsDarwinInstallerIdentifiesSystemAsDarwin(self):
    dotfilesinstaller.identifySystem()
    assert(sys.stdout.getvalue().strip().endswith('Darwin'))

  @mock.patch('platform.system', mock.MagicMock(return_value='Linux'))
  def testWhenSystemIsLinuxInstallerIdentifiesSystemAsLinux(self):
    dotfilesinstaller.identifySystem()
    assert(sys.stdout.getvalue().strip().endswith('Linux'))

  @mock.patch('platform.system', mock.MagicMock(return_value='Windows'))
  def testWhenSystemIsWindowsInstallerIdentifiesSystemAsWindowsAndExitsWithCode1(self):
    with self.assertRaises(SystemExit) as cm:
      dotfilesinstaller.identifySystem()
      assert(sys.stdout.getvalue().strip().endswith('not supported!'))
      assertEqual(cm.exception.code, 1)

  def testInstallerWillDeleteExistingOutputFiles(self):
    dotfilesinstaller.init()
    dotfilesinstaller.identifySystem()
    self.macBashOutputFile = dotfilesinstaller.macBashOutputFile
    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      with open(file,'a') as bash:
        bash.write('Test file...')
    dotfilesinstaller.cleanUp()

    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      assert("Removing " + file in sys.stdout.getvalue().strip())
      self.assertFalse(os.path.isfile(file))

  def testWhenOutputFilesDoNotExistInstallerWillNotAttemptDeletion(self):
    if os.path.isfile(self.macBashOutputFile):
      os.remove(self.macBashOutputFile)
    try:
      dotfilesinstaller.cleanUp()
    except OSError, e:
      if e.errno == 2:
        self.fail("Tried to delete nonexistent file!")

  def testBashOutputFileStartsWithShebang(self):
    dotfilesinstaller.addBashOutputFileHeader()
    with open(self.macBashOutputFile,'r') as bashrc:
      self.assertEquals(bashrc.readline(), "#!/bin/bash\n")

  def testBashInputFileContentsAreWrittenToOutputFile(self):
    self.inputFileMock = io.StringIO(u'some_token=some_value\n')
    dotfilesinstaller.addInputFileContents(self.inputFileMock, False)
    foundExpectedResult = False
    mock = self.inputFileMock.getvalue()
    with open(self.macBashOutputFile,'r') as bashrc:
      result = bashrc.read()
    self.assertTrue(result in mock)

  def setUpSymlink(self):
    with open(self.symlinkTarget,'a') as bar:
      dotfilesinstaller.createSymlink(self.symlinkTarget, 'foo')
    self.createdSymlink = dotfilesinstaller.homeDir + 'foo'

  def testWhenSymlinkDoesNotExistItGetsCreated(self):
    self.setUpSymlink()
    try:
      os.stat(self.createdSymlink)
    except OSError:
      self.fail("Symlink " + self.createdSymlink + " not created!")

  def testWhenSymlinkExistsButIsBrokenItGetsDeletedAndReCreated(self):
    dotfilesinstaller.createSymlink(self.symlinkTarget, 'foo')
    dotfilesinstaller.createSymlink(self.macBashOutputFile, 'foo')
    assert("Link is broken." in sys.stdout.getvalue().strip())
    assert("Link created." in sys.stdout.getvalue().strip())

  def testWhenSymlinkExistsAndIsValidItDoesNotGetDeleted(self):
    self.setUpSymlink()
    dotfilesinstaller.createSymlink('bar', 'foo')
    assert("Link is valid." in sys.stdout.getvalue().strip())

  def testBashOutputFileDoesNotContainBashPrivateTokens(self):
    if not os.path.isfile('bash_private'):
      with open('bash_private','w') as bashPrivate:
        bashPrivate.write('foo=bar')
    with open('bash_private','r') as bashPrivate:
      dotfilesinstaller.install()
      with open(self.macBashOutputFile,'r') as bashrc:
        assert(bashPrivate.read() not in bashrc.read())

  def testAllOutputFilesExistAfterInstallation(self):
    dotfilesinstaller.main()
    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      if not os.path.isfile(file):
	self.fail("Did not find expected output file: "  + file)

  def testLinuxTokensNotInMacBashOutputFile(self):
    dotfilesinstaller.main()
    with open(self.macBashOutputFile,'r') as macBashOutput:
      with open('bash_linux','r') as bashLinux:
        linuxContents = bashLinux.read()
        macContents = macBashOutput.read()
        assert(linuxContents not in macContents)

suite = unittest.TestLoader().loadTestsFromTestCase(DotfilesTest)
unittest.main(module=__name__, buffer=True, exit=False)
<commit_msg>Rename module file.<commit_after>#!/usr/bin/python

import unittest
import mock
import dotfiles
import platform
import sys
import os
import io
import time

class DotfilesTest(unittest.TestCase):

  def setUp(self):
    dotfiles.init()
    dotfiles.identifySystem()
    dotfiles.cleanUp()
    self.symlinkTarget = 'bar'
    self.macBashOutputFile = dotfiles.macBashOutputFile
    self.macBashOutputDotFile = '.' + self.macBashOutputFile
    self.linuxBashOutputFile = dotfiles.linuxBashOutputFile
    self.linuxBashOutputDotFile = '.' + self.linuxBashOutputFile

  def tearDown(self):
    self.createdSymlink = dotfiles.homeDir + 'foo'
    if os.path.islink(self.createdSymlink):
      os.remove(self.createdSymlink)
    if os.path.isfile(self.symlinkTarget):
      os.remove(self.symlinkTarget)
    dotfiles.cleanUp()

  @mock.patch('platform.system', mock.MagicMock(return_value='Darwin'))
  def testWhenSystemIsDarwinInstallerIdentifiesSystemAsDarwin(self):
    dotfiles.identifySystem()
    assert(sys.stdout.getvalue().strip().endswith('Darwin'))

  @mock.patch('platform.system', mock.MagicMock(return_value='Linux'))
  def testWhenSystemIsLinuxInstallerIdentifiesSystemAsLinux(self):
    dotfiles.identifySystem()
    assert(sys.stdout.getvalue().strip().endswith('Linux'))

  @mock.patch('platform.system', mock.MagicMock(return_value='Windows'))
  def testWhenSystemIsWindowsInstallerIdentifiesSystemAsWindowsAndExitsWithCode1(self):
    with self.assertRaises(SystemExit) as cm:
      dotfiles.identifySystem()
      assert(sys.stdout.getvalue().strip().endswith('not supported!'))
      assertEqual(cm.exception.code, 1)

  def testInstallerWillDeleteExistingOutputFiles(self):
    dotfiles.init()
    dotfiles.identifySystem()
    self.macBashOutputFile = dotfiles.macBashOutputFile
    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      with open(file,'a') as bash:
        bash.write('Test file...')
    dotfiles.cleanUp()

    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      assert("Removing " + file in sys.stdout.getvalue().strip())
      self.assertFalse(os.path.isfile(file))

  def testWhenOutputFilesDoNotExistInstallerWillNotAttemptDeletion(self):
    if os.path.isfile(self.macBashOutputFile):
      os.remove(self.macBashOutputFile)
    try:
      dotfiles.cleanUp()
    except OSError, e:
      if e.errno == 2:
        self.fail("Tried to delete nonexistent file!")

  def testBashOutputFileStartsWithShebang(self):
    dotfiles.addBashOutputFileHeader()
    with open(self.macBashOutputFile,'r') as bashrc:
      self.assertEquals(bashrc.readline(), "#!/bin/bash\n")

  def testBashInputFileContentsAreWrittenToOutputFile(self):
    self.inputFileMock = io.StringIO(u'some_token=some_value\n')
    dotfiles.addInputFileContents(self.inputFileMock, False)
    foundExpectedResult = False
    mock = self.inputFileMock.getvalue()
    with open(self.macBashOutputFile,'r') as bashrc:
      result = bashrc.read()
    self.assertTrue(result in mock)

  def setUpSymlink(self):
    with open(self.symlinkTarget,'a') as bar:
      dotfiles.createSymlink(self.symlinkTarget, 'foo')
    self.createdSymlink = dotfiles.homeDir + 'foo'

  def testWhenSymlinkDoesNotExistItGetsCreated(self):
    self.setUpSymlink()
    try:
      os.stat(self.createdSymlink)
    except OSError:
      self.fail("Symlink " + self.createdSymlink + " not created!")

  def testWhenSymlinkExistsButIsBrokenItGetsDeletedAndReCreated(self):
    dotfiles.createSymlink(self.symlinkTarget, 'foo')
    dotfiles.createSymlink(self.macBashOutputFile, 'foo')
    assert("Link is broken." in sys.stdout.getvalue().strip())
    assert("Link created." in sys.stdout.getvalue().strip())

  def testWhenSymlinkExistsAndIsValidItDoesNotGetDeleted(self):
    self.setUpSymlink()
    dotfiles.createSymlink('bar', 'foo')
    assert("Link is valid." in sys.stdout.getvalue().strip())

  def testBashOutputFileDoesNotContainBashPrivateTokens(self):
    if not os.path.isfile('bash_private'):
      with open('bash_private','w') as bashPrivate:
        bashPrivate.write('foo=bar')
    with open('bash_private','r') as bashPrivate:
      dotfiles.install()
      with open(self.macBashOutputFile,'r') as bashrc:
        assert(bashPrivate.read() not in bashrc.read())

  def testAllOutputFilesExistAfterInstallation(self):
    dotfiles.main()
    for file in [self.macBashOutputFile, self.macBashOutputDotFile, self.linuxBashOutputFile, self.linuxBashOutputDotFile]:
      if not os.path.isfile(file):
	self.fail("Did not find expected output file: "  + file)

  def testLinuxTokensNotInMacBashOutputFile(self):
    dotfiles.main()
    with open(self.macBashOutputFile,'r') as macBashOutput:
      with open('bash_linux','r') as bashLinux:
        linuxContents = bashLinux.read()
        macContents = macBashOutput.read()
        assert(linuxContents not in macContents)

suite = unittest.TestLoader().loadTestsFromTestCase(DotfilesTest)
unittest.main(module=__name__, buffer=True, exit=False)
<|endoftext|>


<commit_before>
# parsetab.py
# This file is automatically generated. Do not edit.
_tabversion = '3.8'

_lr_method = 'LALR'

_lr_signature = '55C53B9434ED5FC68BBE31AE8D09ED67'

_lr_action_items = {'INPUT':([12,],[20,]),'OP_SUB':([12,],[21,]),'LPAREN':([0,3,5,6,7,9,10,11,13,14,15,16,18,19,20,21,22,23,24,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,46,47,48,49,50,51,52,54,55,57,58,],[4,-4,-5,4,12,-3,-2,12,-27,-9,-26,-28,-8,-7,31,12,12,31,12,12,12,-6,-15,-17,-11,31,12,-30,12,-13,31,12,12,12,-21,12,-14,-10,-29,-23,-22,-12,-24,-20,-25,-19,-16,]),'IMPORT':([4,],[8,]),'$end':([1,3,5,6,9,10,19,28,],[0,-4,-5,-1,-3,-2,-7,-6,]),'RPAREN':([13,15,16,17,20,23,26,29,30,32,33,34,35,36,37,38,39,41,42,43,46,47,48,49,50,51,52,53,54,55,56,57,58,],[-27,-26,-28,28,32,37,42,-15,-17,-11,47,49,-30,50,-13,51,52,54,-21,55,-14,-10,-29,-23,-22,-12,-24,57,-20,-25,58,-19,-16,]),'OP_ADD':([12,],[22,]),'OUTPUT':([12,],[23,]),'RBRACE':([11,13,14,15,16,18,32,37,42,47,49,50,51,52,54,55,57,],[19,-27,-9,-26,-28,-8,-11,-13,-21,-10,-23,-22,-12,-24,-20,-25,-19,]),'ASSIGN':([12,],[25,]),'LBRACE':([0,3,5,6,9,10,19,28,],[2,-4,-5,2,-3,-2,-7,-6,]),'OP_MUL':([12,],[24,]),'ID':([2,7,8,11,12,13,14,15,16,18,20,21,22,23,24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,54,55,57,58,],[7,15,17,15,26,-27,-9,-26,-28,-8,30,15,15,30,15,40,15,15,-15,-17,45,-11,30,15,-30,15,-13,30,15,15,15,-21,15,56,-18,-14,-10,-29,-23,-22,-12,-24,-20,-25,-19,-16,]),'STRING':([7,11,13,14,15,16,18,21,22,24,26,27,32,34,35,36,37,39,40,41,42,43,47,48,49,50,51,52,54,55,57,],[16,16,-27,-9,-26,-28,-8,16,16,16,16,16,-11,16,-30,16,-13,16,16,16,-21,16,-10,-29,-23,-22,-12,-24,-20,-25,-19,]),'NUMBER':([7,11,13,14,15,16,18,21,22,24,26,27,32,34,35,36,37,39,40,41,42,43,47,48,49,50,51,52,54,55,57,],[13,13,-27,-9,-26,-28,-8,13,13,13,13,13,-11,13,-30,13,-13,13,13,13,-21,13,-10,-29,-23,-22,-12,-24,-20,-25,-19,]),'OP_DIV':([12,],[27,]),}

_lr_action = {}
for _k, _v in _lr_action_items.items():
   for _x,_y in zip(_v[0],_v[1]):
      if not _x in _lr_action:  _lr_action[_x] = {}
      _lr_action[_x][_k] = _y
del _lr_action_items

_lr_goto_items = {'type':([31,],[44,]),'program':([0,],[1,]),'expression':([7,11,21,22,24,26,27,34,36,39,40,41,43,],[14,18,35,35,35,35,35,48,48,48,53,48,48,]),'parameter_list':([21,22,24,26,27,],[34,36,39,41,43,]),'declaration':([20,23,33,38,],[29,29,46,46,]),'import_statement':([0,6,],[3,9,]),'expression_list':([7,],[11,]),'component':([0,6,],[5,10,]),'statement_list':([0,],[6,]),'declaration_list':([20,23,],[33,38,]),}

_lr_goto = {}
for _k, _v in _lr_goto_items.items():
   for _x, _y in zip(_v[0], _v[1]):
       if not _x in _lr_goto: _lr_goto[_x] = {}
       _lr_goto[_x][_k] = _y
del _lr_goto_items
_lr_productions = [
  ("S' -> program","S'",1,None,None,None),
  ('program -> statement_list','program',1,'p_program','parser.py',8),
  ('statement_list -> statement_list component','statement_list',2,'p_statement_list','parser.py',13),
  ('statement_list -> statement_list import_statement','statement_list',2,'p_statement_list','parser.py',14),
  ('statement_list -> import_statement','statement_list',1,'p_statement_list','parser.py',15),
  ('statement_list -> component','statement_list',1,'p_statement_list','parser.py',16),
  ('import_statement -> LPAREN IMPORT ID RPAREN','import_statement',4,'p_import','parser.py',26),
  ('component -> LBRACE ID expression_list RBRACE','component',4,'p_component','parser.py',30),
  ('expression_list -> expression_list expression','expression_list',2,'p_expression_list','parser.py',34),
  ('expression_list -> expression','expression_list',1,'p_expression_list','parser.py',35),
  ('expression -> LPAREN INPUT declaration_list RPAREN','expression',4,'p_inputExpr','parser.py',43),
  ('expression -> LPAREN INPUT RPAREN','expression',3,'p_inputExpr','parser.py',44),
  ('expression -> LPAREN OUTPUT declaration_list RPAREN','expression',4,'p_outputExpr','parser.py',51),
  ('expression -> LPAREN OUTPUT RPAREN','expression',3,'p_outputExpr','parser.py',52),
  ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','parser.py',59),
  ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','parser.py',60),
  ('declaration -> LPAREN type ID RPAREN','declaration',4,'p_id','parser.py',68),
  ('declaration -> ID','declaration',1,'p_id','parser.py',69),
  ('type -> ID','type',1,'p_type_id','parser.py',76),
  ('expression -> LPAREN ASSIGN ID expression RPAREN','expression',5,'p_assignExpr','parser.py',80),
  ('expression -> LPAREN ID parameter_list RPAREN','expression',4,'p_named_function_operation','parser.py',84),
  ('expression -> LPAREN ID RPAREN','expression',3,'p_named_function_operation','parser.py',85),
  ('expression -> LPAREN OP_ADD parameter_list RPAREN','expression',4,'p_op_add_expression','parser.py',92),
  ('expression -> LPAREN OP_SUB parameter_list RPAREN','expression',4,'p_op_sub_expression','parser.py',95),
  ('expression -> LPAREN OP_MUL parameter_list RPAREN','expression',4,'p_op_mul_expression','parser.py',98),
  ('expression -> LPAREN OP_DIV parameter_list RPAREN','expression',4,'p_op_div_expression','parser.py',101),
  ('expression -> ID','expression',1,'p_expression_id','parser.py',105),
  ('expression -> NUMBER','expression',1,'p_number','parser.py',109),
  ('expression -> STRING','expression',1,'p_string','parser.py',113),
  ('parameter_list -> parameter_list expression','parameter_list',2,'p_parameter_list','parser.py',117),
  ('parameter_list -> expression','parameter_list',1,'p_parameter_list','parser.py',118),
]
<commit_msg>resetting parsetab.py to original version<commit_after>
# parsetab.py
# This file is automatically generated. Do not edit.
_tabversion = '3.8'

_lr_method = 'LALR'

_lr_signature = '371EF72B57DF7D01BB0A145AD73D5ADC'

_lr_action_items = {'$end':([2,3,5,6,8,9,19,28,],[-1,0,-4,-5,-3,-2,-7,-6,]),'OP_MUL':([13,],[25,]),'INPUT':([13,],[27,]),'LPAREN':([0,2,5,6,7,8,9,11,12,14,15,16,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,41,42,43,44,45,47,48,49,50,53,54,55,56,58,],[4,4,-4,-5,13,-3,-2,13,-9,-26,-27,-28,-8,-7,13,13,13,13,13,40,40,-6,13,-30,-21,13,13,13,13,13,-13,-17,-15,40,-11,40,-29,-20,-25,-22,-23,-24,-12,-14,-10,-19,-16,]),'OP_DIV':([13,],[22,]),'ASSIGN':([13,],[21,]),'OP_SUB':([13,],[24,]),'OP_ADD':([13,],[23,]),'ID':([1,7,10,11,12,13,14,15,16,18,20,21,22,23,24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,47,48,49,50,51,52,53,54,55,56,58,],[7,14,17,14,-9,20,-26,-27,-28,-8,14,32,14,14,14,14,38,38,14,-30,-21,14,14,14,14,14,-13,-17,-15,52,38,-11,38,-29,-20,-25,-22,-23,-24,57,-18,-12,-14,-10,-19,-16,]),'IMPORT':([4,],[10,]),'NUMBER':([7,11,12,14,15,16,18,20,22,23,24,25,29,30,31,32,33,34,35,36,37,42,44,45,47,48,49,50,53,55,56,],[15,15,-9,-26,-27,-28,-8,15,15,15,15,15,15,-30,-21,15,15,15,15,15,-13,-11,-29,-20,-25,-22,-23,-24,-12,-10,-19,]),'LBRACE':([0,2,5,6,8,9,19,28,],[1,1,-4,-5,-3,-2,-7,-6,]),'OUTPUT':([13,],[26,]),'RBRACE':([11,12,14,15,16,18,31,37,42,45,47,48,49,50,53,55,56,],[19,-9,-26,-27,-28,-8,-21,-13,-11,-20,-25,-22,-23,-24,-12,-10,-19,]),'RPAREN':([14,15,16,17,20,26,27,29,30,31,33,34,35,36,37,38,39,41,42,43,44,45,46,47,48,49,50,53,54,55,56,57,58,],[-26,-27,-28,28,31,37,42,45,-30,-21,47,48,49,50,-13,-17,-15,53,-11,55,-29,-20,56,-25,-22,-23,-24,-12,-14,-10,-19,58,-16,]),'STRING':([7,11,12,14,15,16,18,20,22,23,24,25,29,30,31,32,33,34,35,36,37,42,44,45,47,48,49,50,53,55,56,],[16,16,-9,-26,-27,-28,-8,16,16,16,16,16,16,-30,-21,16,16,16,16,16,-13,-11,-29,-20,-25,-22,-23,-24,-12,-10,-19,]),}

_lr_action = {}
for _k, _v in _lr_action_items.items():
   for _x,_y in zip(_v[0],_v[1]):
      if not _x in _lr_action:  _lr_action[_x] = {}
      _lr_action[_x][_k] = _y
del _lr_action_items

_lr_goto_items = {'type':([40,],[51,]),'expression_list':([7,],[11,]),'declaration_list':([26,27,],[41,43,]),'parameter_list':([20,22,23,24,25,],[29,33,34,35,36,]),'expression':([7,11,20,22,23,24,25,29,32,33,34,35,36,],[12,18,30,30,30,30,30,44,46,44,44,44,44,]),'declaration':([26,27,41,43,],[39,39,54,54,]),'program':([0,],[3,]),'statement_list':([0,],[2,]),'import_statement':([0,2,],[5,8,]),'component':([0,2,],[6,9,]),}

_lr_goto = {}
for _k, _v in _lr_goto_items.items():
   for _x, _y in zip(_v[0], _v[1]):
       if not _x in _lr_goto: _lr_goto[_x] = {}
       _lr_goto[_x][_k] = _y
del _lr_goto_items
_lr_productions = [
  ("S' -> program","S'",1,None,None,None),
  ('program -> statement_list','program',1,'p_program','parser.py',8),
  ('statement_list -> statement_list component','statement_list',2,'p_statement_list','parser.py',13),
  ('statement_list -> statement_list import_statement','statement_list',2,'p_statement_list','parser.py',14),
  ('statement_list -> import_statement','statement_list',1,'p_statement_list','parser.py',15),
  ('statement_list -> component','statement_list',1,'p_statement_list','parser.py',16),
  ('import_statement -> LPAREN IMPORT ID RPAREN','import_statement',4,'p_import','parser.py',26),
  ('component -> LBRACE ID expression_list RBRACE','component',4,'p_component','parser.py',30),
  ('expression_list -> expression_list expression','expression_list',2,'p_expression_list','parser.py',34),
  ('expression_list -> expression','expression_list',1,'p_expression_list','parser.py',35),
  ('expression -> LPAREN INPUT declaration_list RPAREN','expression',4,'p_inputExpr','parser.py',43),
  ('expression -> LPAREN INPUT RPAREN','expression',3,'p_inputExpr','parser.py',44),
  ('expression -> LPAREN OUTPUT declaration_list RPAREN','expression',4,'p_outputExpr','parser.py',51),
  ('expression -> LPAREN OUTPUT RPAREN','expression',3,'p_outputExpr','parser.py',52),
  ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','parser.py',59),
  ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','parser.py',60),
  ('declaration -> LPAREN type ID RPAREN','declaration',4,'p_id','parser.py',68),
  ('declaration -> ID','declaration',1,'p_id','parser.py',69),
  ('type -> ID','type',1,'p_type_id','parser.py',76),
  ('expression -> LPAREN ASSIGN ID expression RPAREN','expression',5,'p_assignExpr','parser.py',80),
  ('expression -> LPAREN ID parameter_list RPAREN','expression',4,'p_named_function_operation','parser.py',84),
  ('expression -> LPAREN ID RPAREN','expression',3,'p_named_function_operation','parser.py',85),
  ('expression -> LPAREN OP_ADD parameter_list RPAREN','expression',4,'p_op_add_expression','parser.py',92),
  ('expression -> LPAREN OP_SUB parameter_list RPAREN','expression',4,'p_op_sub_expression','parser.py',95),
  ('expression -> LPAREN OP_MUL parameter_list RPAREN','expression',4,'p_op_mul_expression','parser.py',98),
  ('expression -> LPAREN OP_DIV parameter_list RPAREN','expression',4,'p_op_div_expression','parser.py',101),
  ('expression -> ID','expression',1,'p_expression_id','parser.py',105),
  ('expression -> NUMBER','expression',1,'p_number','parser.py',109),
  ('expression -> STRING','expression',1,'p_string','parser.py',113),
  ('parameter_list -> parameter_list expression','parameter_list',2,'p_parameter_list','parser.py',117),
  ('parameter_list -> expression','parameter_list',1,'p_parameter_list','parser.py',118),
]
<|endoftext|>


<commit_before>import azurerm
import json

# Load Azure app defaults
try:
   with open('azurermconfig.json') as configFile:
      configData = json.load(configFile)
except FileNotFoundError:
   print("Error: Expecting vmssConfig.json in current folder")
   sys.exit()

tenant_id = configData['tenantId']
app_id = configData['appId']
app_secret = configData['appSecret']
subscription_id = configData['subscriptionId']

access_token = azurerm.get_access_token(tenant_id, app_id, app_secret)

# list locations
locations = azurerm.list_locations(access_token, subscription_id)

for location in locations['value']:
    print(location['name']
          + ', Display Name: ' + location['displayName']
          + ', Coords: ' + location['latitude']
          + ', ' + location['longitude'])

<commit_msg>Reformat list_locations.py example<commit_after>'''list_locations.py - list Azure locations'''
import json
import sys
import azurerm

def main():
    '''Main routine.'''
    # Load Azure app defaults
    try:
        with open('azurermconfig.json') as config_file:
            config_data = json.load(config_file)
    except FileNotFoundError:
        sys.exit("Error: Expecting azurermconfig.json in current folder")

    tenant_id = config_data['tenantId']
    app_id = config_data['appId']
    app_secret = config_data['appSecret']
    subscription_id = config_data['subscriptionId']

    access_token = azurerm.get_access_token(tenant_id, app_id, app_secret)

    # list locations
    locations = azurerm.list_locations(access_token, subscription_id)

    for location in locations['value']:
        print(location['name']
              + ', Display Name: ' + location['displayName']
              + ', Coords: ' + location['latitude']
              + ', ' + location['longitude'])

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import logging

from src.shared import messages
from src.shared.game_state import GameState
from src.shared.ident import UnitId, unitToPlayer, getUnitSubId
from src.shared.logconfig import newLogger
from src.shared.unit_set import UnitSet
from src.shared.message_infrastructure import deserializeMessage, \
    unhandledInternalMessage, InvalidMessageError
from src.client import messages as cmessages

# Constants
GRAPHICS_SCALE = 3

# Logging
log = newLogger(__name__)

class Backend:
    def __init__(self, done):
        # Done is a Twisted Deferred object whose callback can be fired to
        # close down the client cleanly (in theory...).
        self.done = done

        self.stdio    = None
        self.network  = None
        self.graphicsInterface = None

        self.allReady = False

        self.myId = -1

        # FIXME [#3]: Magic numbers very bad!! >:(
        self.gameState = GameState((10, 5))
        self.unitSelection = UnitSet()

    @property
    def allComponents(self):
        return (self.stdio, self.network, self.graphicsInterface)

    ########################################################################
    # Initialization

    # All wings, report in.

    # Red 2, standing by.
    def stdioReady(self, stdioComponent):
        assert self.stdio is None
        self.stdio = stdioComponent
        assert self.stdio is not None
        self.checkIfAllReady()

    # Red 11, standing by.
    def networkReady(self, networkComponent):
        assert self.network is None
        self.network = networkComponent
        assert self.network is not None
        self.checkIfAllReady()

    # Red 5, standing by.
    def graphicsInterfaceReady(self, graphicsInterface):
        assert self.graphicsInterface is None
        self.graphicsInterface = graphicsInterface
        assert self.graphicsInterface is not None
        self.checkIfAllReady()

    def checkIfAllReady(self):
        for component in self.allComponents:
            if component is None:
                # Not ready
                return
        self.allComponentsReady()

    def allComponentsReady(self):
        # I'm keeping this method around, even though all it does is set a
        # flag, because it seems like we might want to put some final
        # initialization code in here.
        # ... or change the implementation somehow so that we don't have to
        # constantly check self.allReady for the entire lifetime of the client.
        self.allReady = True

        # Request obelisks.
        msg = messages.OrderNew((10, 0))
        self.network.backendMessage(msg.serialize())
        msg = messages.OrderNew((0, 10))
        self.network.backendMessage(msg.serialize())

    ########################################################################
    # Incoming messages

    def stdioMessage(self, message):
        if self.allReady:
            self.handleUserInput(message)
        else:
            # TODO: Buffer messages until ready? Don't just drop them....
            log.warning("Input '{}' ignored; client not initialized yet." \
                .format(message))

    def handleUserInput(self, message):
        assert not message.endswith('\n')

        if message == "dump":
            log.info("Dumping all unit info...")
            for uid in sorted(self.gameState.positions):
                pos = self.gameState.positions[uid]
                isSelected = (uid in self.unitSelection)
                log.info("    {sel:1} {player:>2}: {subId:>3} @ {x:>4}, {y:>4}"
                         .format(sel    = "*" if isSelected else "",
                                 player = unitToPlayer(uid),
                                 subId  = getUnitSubId(uid),
                                 x      = pos[0],
                                 y      = pos[1]))
            log.info("End unit dump.")
        else:
            self.network.backendMessage(message)

    def networkMessage(self, messageStr):
        if self.allReady:
            try:
                message = deserializeMessage(messageStr)
                self.tryToHandleNetworkMessage(message)
            except InvalidMessageError as error:
                illFormedMessage(error, log, sender="server")

            # Regardless of whether we were able to handle it, forward the
            # message on to the graphicsInterface (which -- at least for now --
            # also handles the YourIdIs message).
            self.graphicsInterface.backendMessage(messageStr)
        else:
            # TODO: Buffer messages until ready? Don't just drop them....
            log.warning("Server message '{}' ignored; client not " \
                "initialized yet.".format(messageStr))

    def tryToHandleNetworkMessage(self, message):
        if isinstance(message, messages.YourIdIs):
            if self.myId >= 0:
                raise RuntimeError("ID already set; can't change it now.")
            self.myId = message.playerId
            log.info("Your id is {id}.".format(id=self.myId))

            # FIXME[#16]: This might be a nonexistent id. We shouldn't issue
            # orders to it unless that unit id actually exists.
            firstId = UnitId(self.myId, 0)
            self.unitSelection = UnitSet([firstId])
        elif isinstance(message, messages.NewObelisk):
            uid = message.unitId
            pos = message.pos
            if uid in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="uid {} already in use".format(uid))
                return
            self.gameState.positions[uid] = pos
        elif isinstance(message, messages.DeleteObelisk):
            uid = message.unitId
            if uid not in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="No such uid: {}".format(uid))
                return
            if uid in self.unitSelection:
                self.removeFromSelection(uid)
            del self.gameState.positions[uid]
        elif isinstance(message, messages.SetPos):
            uid = message.unitId
            pos = message.pos
            if uid not in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="No such uid: {}".format(uid))
                return
            self.gameState.positions[uid] = pos
        elif isinstance(message, messages.GroundInfo):
            # Note: this isn't used in any way right now. I think it's right,
            # but it's definitely possible we transposed something, or worse.
            x, y        = message.pos
            terrainType = message.terrainType
            self.gameState.groundTypes[x][y] = terrainType
        else:
            # It's okay if we aren't able to handle a message from the
            # server; maybe the GraphicsInterface will handle it.
            pass

    def graphicsMessage(self, messageStr):
        message = deserializeMessage(messageStr)
        if isinstance(message, cmessages.Click):
            # TODO: Have Click indicate "left" or "right", rather than just a
            # numerical button.
            # TODO: GraphicsInterface should probably translate Click.pos to a
            # uPos before passing it to the backend.
            if message.button == 1:
                # Left mouse button
                gPos = message.pos
                uPos = graphicsToUnit(gPos)
                chosenUnit = self.getUnitAt(uPos)
                self.clearSelection()
                if chosenUnit is not None:
                    self.addToSelection(chosenUnit)
            elif message.button == 3:
                # Right mouse button
                newMsg = messages.OrderMove(self.unitSelection,
                                            graphicsToUnit(message.pos))
                self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.ShiftLClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None:
                self.addToSelection(chosenUnit)
        elif isinstance(message, cmessages.ControlLClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None and chosenUnit in self.unitSelection:
                self.removeFromSelection(chosenUnit)
        elif isinstance(message, cmessages.ShiftRClick):
            newMsg = messages.OrderNew(graphicsToUnit(message.pos))
            self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.ControlRClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None:
                if chosenUnit in self.unitSelection:
                    self.removeFromSelection(chosenUnit)
                newMsg = messages.OrderDel(UnitSet([chosenUnit]))
                self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.DragBox):
            ux1, uy1 = graphicsToUnit(message.corner1)
            ux2, uy2 = graphicsToUnit(message.corner2)
            xMin = min(ux1, ux2)
            xMax = max(ux1, ux2)
            yMin = min(uy1, uy2)
            yMax = max(uy1, uy2)
            self.clearSelection()
            for (uid, uPos) in self.gameState.positions.iteritems():
                ux, uy = uPos
                # TODO: Add a tolerance based on the size of the unit.
                if xMin <= ux <= xMax and yMin <= uy <= yMax:
                    self.addToSelection(uid)
        elif isinstance(message, cmessages.RequestCenter):
            if not self.unitSelection:
                # FIXME: Move to center of world.
                return
            totalX, totalY = 0, 0
            for unitId in self.unitSelection:
                unitX, unitY = self.gameState.positions[unitId]
                totalX += unitX
                totalY += unitY
            centroid = (totalX // len(self.unitSelection),
                        totalY // len(self.unitSelection))
            # FIXME: Write this.
        elif isinstance(message, cmessages.RequestQuit):
            for component in self.allComponents:
                component.cleanup()
            self.done.callback(None)
        else:
            unhandledInternalMessage(message, log)

    def getUnitAt(self, targetUPos):
        targetX, targetY = targetUPos

        # TODO[#3]: Make this a real constant somewhere. Or take into account
        # the size of the unit? This could get ugly.
        MAX_DISTANCE = 30**2

        nearest = None
        # TODO: Compute the diameter of the world in unit coordinates when we
        # first load the map, have the server send that to the client, save
        # that+1 as UPOS_INFINITY or some such, and use that here instead of a
        # float.
        nearestDistance = float('inf')
        for (uid, uPos) in self.gameState.positions.iteritems():
            if unitToPlayer(uid) != self.myId:
                continue

            x, y = uPos
            distance = (x - targetX)**2 + (y - targetY)**2
            if distance < nearestDistance and distance < MAX_DISTANCE:
                nearest         = uid
                nearestDistance = distance

        return nearest

    def addToSelection(self, unitId):
        self.unitSelection.add(unitId)
        msg = cmessages.MarkUnitSelected(unitId, True)
        self.graphicsInterface.backendMessage(msg.serialize())

    def removeFromSelection(self, unitId):
        self.unitSelection.remove(unitId)
        msg = cmessages.MarkUnitSelected(unitId, False)
        self.graphicsInterface.backendMessage(msg.serialize())

    def clearSelection(self):
        for unitId in self.unitSelection:
            msg = cmessages.MarkUnitSelected(unitId, False)
            self.graphicsInterface.backendMessage(msg.serialize())
        self.unitSelection = UnitSet()

# TODO: Shouldn't these be in the GraphicsInterface if they're going to be
# with a single component?
def unitToGraphics(unitCoords):
    """Convert unit (xu,yu) integers tuples to graphics (xg,yg) float tuples
    """
    return tuple(float(x)/GRAPHICS_SCALE for x in unitCoords)

def graphicsToUnit(graphicsCoords):
    """Convert graphics (xg,yg) float tuples to unit (xu,yu) integers tuples
    """
    return tuple(int(round(x*GRAPHICS_SCALE)) for x in graphicsCoords)

<commit_msg>Update a FIXME.<commit_after>import logging

from src.shared import messages
from src.shared.game_state import GameState
from src.shared.ident import UnitId, unitToPlayer, getUnitSubId
from src.shared.logconfig import newLogger
from src.shared.unit_set import UnitSet
from src.shared.message_infrastructure import deserializeMessage, \
    unhandledInternalMessage, InvalidMessageError
from src.client import messages as cmessages

# Constants
GRAPHICS_SCALE = 3

# Logging
log = newLogger(__name__)

class Backend:
    def __init__(self, done):
        # Done is a Twisted Deferred object whose callback can be fired to
        # close down the client cleanly (in theory...).
        self.done = done

        self.stdio    = None
        self.network  = None
        self.graphicsInterface = None

        self.allReady = False

        self.myId = -1

        # FIXME [#62]: Get the size from the server! Magic numbers very bad!!
        self.gameState = GameState((10, 5))
        self.unitSelection = UnitSet()

    @property
    def allComponents(self):
        return (self.stdio, self.network, self.graphicsInterface)

    ########################################################################
    # Initialization

    # All wings, report in.

    # Red 2, standing by.
    def stdioReady(self, stdioComponent):
        assert self.stdio is None
        self.stdio = stdioComponent
        assert self.stdio is not None
        self.checkIfAllReady()

    # Red 11, standing by.
    def networkReady(self, networkComponent):
        assert self.network is None
        self.network = networkComponent
        assert self.network is not None
        self.checkIfAllReady()

    # Red 5, standing by.
    def graphicsInterfaceReady(self, graphicsInterface):
        assert self.graphicsInterface is None
        self.graphicsInterface = graphicsInterface
        assert self.graphicsInterface is not None
        self.checkIfAllReady()

    def checkIfAllReady(self):
        for component in self.allComponents:
            if component is None:
                # Not ready
                return
        self.allComponentsReady()

    def allComponentsReady(self):
        # I'm keeping this method around, even though all it does is set a
        # flag, because it seems like we might want to put some final
        # initialization code in here.
        # ... or change the implementation somehow so that we don't have to
        # constantly check self.allReady for the entire lifetime of the client.
        self.allReady = True

        # Request obelisks.
        msg = messages.OrderNew((10, 0))
        self.network.backendMessage(msg.serialize())
        msg = messages.OrderNew((0, 10))
        self.network.backendMessage(msg.serialize())

    ########################################################################
    # Incoming messages

    def stdioMessage(self, message):
        if self.allReady:
            self.handleUserInput(message)
        else:
            # TODO: Buffer messages until ready? Don't just drop them....
            log.warning("Input '{}' ignored; client not initialized yet." \
                .format(message))

    def handleUserInput(self, message):
        assert not message.endswith('\n')

        if message == "dump":
            log.info("Dumping all unit info...")
            for uid in sorted(self.gameState.positions):
                pos = self.gameState.positions[uid]
                isSelected = (uid in self.unitSelection)
                log.info("    {sel:1} {player:>2}: {subId:>3} @ {x:>4}, {y:>4}"
                         .format(sel    = "*" if isSelected else "",
                                 player = unitToPlayer(uid),
                                 subId  = getUnitSubId(uid),
                                 x      = pos[0],
                                 y      = pos[1]))
            log.info("End unit dump.")
        else:
            self.network.backendMessage(message)

    def networkMessage(self, messageStr):
        if self.allReady:
            try:
                message = deserializeMessage(messageStr)
                self.tryToHandleNetworkMessage(message)
            except InvalidMessageError as error:
                illFormedMessage(error, log, sender="server")

            # Regardless of whether we were able to handle it, forward the
            # message on to the graphicsInterface (which -- at least for now --
            # also handles the YourIdIs message).
            self.graphicsInterface.backendMessage(messageStr)
        else:
            # TODO: Buffer messages until ready? Don't just drop them....
            log.warning("Server message '{}' ignored; client not " \
                "initialized yet.".format(messageStr))

    def tryToHandleNetworkMessage(self, message):
        if isinstance(message, messages.YourIdIs):
            if self.myId >= 0:
                raise RuntimeError("ID already set; can't change it now.")
            self.myId = message.playerId
            log.info("Your id is {id}.".format(id=self.myId))

            # FIXME[#16]: This might be a nonexistent id. We shouldn't issue
            # orders to it unless that unit id actually exists.
            firstId = UnitId(self.myId, 0)
            self.unitSelection = UnitSet([firstId])
        elif isinstance(message, messages.NewObelisk):
            uid = message.unitId
            pos = message.pos
            if uid in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="uid {} already in use".format(uid))
                return
            self.gameState.positions[uid] = pos
        elif isinstance(message, messages.DeleteObelisk):
            uid = message.unitId
            if uid not in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="No such uid: {}".format(uid))
                return
            if uid in self.unitSelection:
                self.removeFromSelection(uid)
            del self.gameState.positions[uid]
        elif isinstance(message, messages.SetPos):
            uid = message.unitId
            pos = message.pos
            if uid not in self.gameState.positions:
                invalidMessageArgument(message, log, sender="server",
                    reason="No such uid: {}".format(uid))
                return
            self.gameState.positions[uid] = pos
        elif isinstance(message, messages.GroundInfo):
            # Note: this isn't used in any way right now. I think it's right,
            # but it's definitely possible we transposed something, or worse.
            x, y        = message.pos
            terrainType = message.terrainType
            self.gameState.groundTypes[x][y] = terrainType
        else:
            # It's okay if we aren't able to handle a message from the
            # server; maybe the GraphicsInterface will handle it.
            pass

    def graphicsMessage(self, messageStr):
        message = deserializeMessage(messageStr)
        if isinstance(message, cmessages.Click):
            # TODO: Have Click indicate "left" or "right", rather than just a
            # numerical button.
            # TODO: GraphicsInterface should probably translate Click.pos to a
            # uPos before passing it to the backend.
            if message.button == 1:
                # Left mouse button
                gPos = message.pos
                uPos = graphicsToUnit(gPos)
                chosenUnit = self.getUnitAt(uPos)
                self.clearSelection()
                if chosenUnit is not None:
                    self.addToSelection(chosenUnit)
            elif message.button == 3:
                # Right mouse button
                newMsg = messages.OrderMove(self.unitSelection,
                                            graphicsToUnit(message.pos))
                self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.ShiftLClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None:
                self.addToSelection(chosenUnit)
        elif isinstance(message, cmessages.ControlLClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None and chosenUnit in self.unitSelection:
                self.removeFromSelection(chosenUnit)
        elif isinstance(message, cmessages.ShiftRClick):
            newMsg = messages.OrderNew(graphicsToUnit(message.pos))
            self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.ControlRClick):
            gPos = message.pos
            uPos = graphicsToUnit(gPos)
            chosenUnit = self.getUnitAt(uPos)
            if chosenUnit is not None:
                if chosenUnit in self.unitSelection:
                    self.removeFromSelection(chosenUnit)
                newMsg = messages.OrderDel(UnitSet([chosenUnit]))
                self.network.backendMessage(newMsg.serialize())
        elif isinstance(message, cmessages.DragBox):
            ux1, uy1 = graphicsToUnit(message.corner1)
            ux2, uy2 = graphicsToUnit(message.corner2)
            xMin = min(ux1, ux2)
            xMax = max(ux1, ux2)
            yMin = min(uy1, uy2)
            yMax = max(uy1, uy2)
            self.clearSelection()
            for (uid, uPos) in self.gameState.positions.iteritems():
                ux, uy = uPos
                # TODO: Add a tolerance based on the size of the unit.
                if xMin <= ux <= xMax and yMin <= uy <= yMax:
                    self.addToSelection(uid)
        elif isinstance(message, cmessages.RequestCenter):
            if not self.unitSelection:
                # FIXME: Move to center of world.
                return
            totalX, totalY = 0, 0
            for unitId in self.unitSelection:
                unitX, unitY = self.gameState.positions[unitId]
                totalX += unitX
                totalY += unitY
            centroid = (totalX // len(self.unitSelection),
                        totalY // len(self.unitSelection))
            # FIXME: Write this.
        elif isinstance(message, cmessages.RequestQuit):
            for component in self.allComponents:
                component.cleanup()
            self.done.callback(None)
        else:
            unhandledInternalMessage(message, log)

    def getUnitAt(self, targetUPos):
        targetX, targetY = targetUPos

        # TODO[#3]: Make this a real constant somewhere. Or take into account
        # the size of the unit? This could get ugly.
        MAX_DISTANCE = 30**2

        nearest = None
        # TODO: Compute the diameter of the world in unit coordinates when we
        # first load the map, have the server send that to the client, save
        # that+1 as UPOS_INFINITY or some such, and use that here instead of a
        # float.
        nearestDistance = float('inf')
        for (uid, uPos) in self.gameState.positions.iteritems():
            if unitToPlayer(uid) != self.myId:
                continue

            x, y = uPos
            distance = (x - targetX)**2 + (y - targetY)**2
            if distance < nearestDistance and distance < MAX_DISTANCE:
                nearest         = uid
                nearestDistance = distance

        return nearest

    def addToSelection(self, unitId):
        self.unitSelection.add(unitId)
        msg = cmessages.MarkUnitSelected(unitId, True)
        self.graphicsInterface.backendMessage(msg.serialize())

    def removeFromSelection(self, unitId):
        self.unitSelection.remove(unitId)
        msg = cmessages.MarkUnitSelected(unitId, False)
        self.graphicsInterface.backendMessage(msg.serialize())

    def clearSelection(self):
        for unitId in self.unitSelection:
            msg = cmessages.MarkUnitSelected(unitId, False)
            self.graphicsInterface.backendMessage(msg.serialize())
        self.unitSelection = UnitSet()

# TODO: Shouldn't these be in the GraphicsInterface if they're going to be
# with a single component?
def unitToGraphics(unitCoords):
    """Convert unit (xu,yu) integers tuples to graphics (xg,yg) float tuples
    """
    return tuple(float(x)/GRAPHICS_SCALE for x in unitCoords)

def graphicsToUnit(graphicsCoords):
    """Convert graphics (xg,yg) float tuples to unit (xu,yu) integers tuples
    """
    return tuple(int(round(x*GRAPHICS_SCALE)) for x in graphicsCoords)

<|endoftext|>