<commit_before># -*- coding: utf-8 -*-
"""
Created on Mon Oct 19.

@author: dejh
"""
from __future__ import print_function

import landlab
from landlab import ModelParameterDictionary, Component, FieldError, \
                    FIXED_VALUE_BOUNDARY
from landlab.core.model_parameter_dictionary import MissingKeyError
from landlab.components.flow_routing.lake_mapper import \
    DepressionFinderAndRouter
from landlab.components.flow_routing.route_flow_dn import FlowRouter
from landlab.grid.base import BAD_INDEX_VALUE
import numpy as np

DEFAULT_SLOPE = 1.e-5

class SinkFiller(Component):
    """
    This component identifies depressions in a topographic surface, then fills
    them in in the topography.  No attempt is made to conserve sediment mass.
    User may specify whether the holes should be filled to flat, or with a
    slight gradient (~<10**-5) downwards towards the depression outlet.
    """
    _name = 'HoleFiller'

    _input_var_names = set(['topographic__elevation',
                            ])

    _output_var_names = set(['topographic__elevation',
                             'sediment_fill__depth',
                             ])

    _var_units = {'topographic__elevation': 'm',
                  'sediment_fill__depth': 'm',
                  }

    _var_mapping = {'topographic__elevation': 'node',
                    'sediment_fill__depth': 'node',
                    }

    _var_defs = {'topographic__elevation': 'Surface topographic elevation',
                 'sediment_fill__depth': 'Depth of sediment added at each' +
                                         'node',
                 }

    def __init__(self, grid, input_stream=None, current_time=0.,
                 routing='D8'):
        """
        Constructor assigns a copy of the grid, and calls the initialize
        method.

        Parameters
        ----------
        grid : RasterModelGrid
            A landlab RasterModelGrid.
        input_stream : str, file_like, or ModelParameterDictionary, optional
            ModelParameterDictionary that holds the input parameters.
        current_time : float, optional
            The current time for the mapper.
        routing : 'D8' or 'D4' (optional)
            If grid is a raster type, controls whether fill connectivity can
            occur on diagonals ('D8', default), or only orthogonally ('D4').
            Has no effect if grid is not a raster.
        """
        self._grid = grid
        if routing is not 'D8':
            assert routing is 'D4'
        self._routing = routing
        if ((type(self._grid) is landlab.grid.raster.RasterModelGrid) and
                (routing is 'D8')):
            self._D8 = True
            self.num_nbrs = 8
        else:
            self._D8 = False  # useful shorthand for thia test we do a lot
            if type(self._grid) is landlab.grid.raster.RasterModelGrid:
                self.num_nbrs = 4
        self.initialize(input_stream)

    def initialize(self, input_stream=None):
        """
        The BMI-style initialize method takes an optional input_stream
        parameter, which may be either a ModelParameterDictionary object or
        an input stream from which a ModelParameterDictionary can read values.
        """
        # Create a ModelParameterDictionary for the inputs
        if input_stream is None:
            inputs = None
        elif type(input_stream) == ModelParameterDictionary:
            inputs = input_stream
        else:
            inputs = ModelParameterDictionary(input_stream)

        # Make sure the grid includes elevation data. This means either:
        #  1. The grid has a node field called 'topographic__elevation', or
        #  2. The input file has an item called 'ELEVATION_FIELD_NAME' *and*
        #     a field by this name exists in the grid.
        try:
            self._elev = self._grid.at_node['topographic__elevation']
        except FieldError:
            try:
                self.topo_field_name = inputs.read_string('ELEVATION_' +
                                                          'FIELD_NAME')
            except AttributeError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", you need to pass the')
                print('name of a text input file or ModelParameterDictionary,')
                print('and this file or dictionary needs to include the name')
                print('of another field in your grid that contains your')
                print('elevation data.')
                raise AttributeError
            except MissingKeyError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", your input file (or')
                print('ModelParameterDictionary) must include an entry with')
                print('the key "ELEVATION_FIELD_NAME", which gives the name')
                print('of a field in your grid that contains your elevation')
                print('data.')
                raise MissingKeyError('ELEVATION_FIELD_NAME')
            try:
                self._elev = self._grid.at_node[self.topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      self.topo_field_name)
        else:
            self.topo_field_name = 'topographic__elevation'
        # create the only new output field:
        self.sed_fill_depth = self._grid.add_zeros('node',
                                                   'sediment_fill__depth')

        self._lf = DepressionFinderAndRouter(self._grid, routing=self._routing)
        self._fr = FlowRouter(self._grid)

    def fill_pits(self, apply_slope=None):
        """
        This is the main method. Call it to fill depressions in a starting
        topography.

        Parameters
        ----------
        apply_slope : None, bool, or float
            If a float is provided this is the slope of the surface down
            towards the lake outlet. Supply a small positive number, e.g.,
            1.e-5 (or True, to use this default value).
            A test is performed to ensure applying this slope will not alter
            the drainage structure at the edge of the filled region (i.e.,
            that we are not accidentally reversing the flow direction far
            from the outlet.) The component will automatically decrease the
            (supplied or default) gradient a number of times to try to
            accommodate this, but will eventually raise an OverflowError
            if it can't deal with it. If you pass True, the method will use
            the default value of 1.e-5.

        Return fields
        -------------
        'topographic__elevation' : the updated elevations
        'sediment_fill__depth' : the depth of sediment added at each node
        """
        self.original_elev = self._elev.copy()
        # We need this, as we'll have to do ALL this again if we manage
        # to jack the elevs too high in one of the "subsidiary" lakes.
        # We're going to implement the lake_mapper component to do the heavy
        # lifting here, then delete its fields. This means we first need to
        # test if these fields already exist, in which case, we should *not*
        # delete them!
        existing_fields = {}
        spurious_fields = set()
        set_of_outputs = self._lf.output_var_names | self._fr.output_var_names
        try:
            set_of_outputs.remove(self.topo_field_name)
        except KeyError:
            pass
        for field in set_of_outputs:
            try:
                existing_fields[field] = self._grid.at_node[field].copy()
            except FieldError:  # not there; good!
                spurious_fields.add(field)

        self._fr.route_flow(method=self._routing)
        self._lf.map_depressions(pits=self._grid.at_node['flow_sinks'],
                                 reroute_flow=False)
        # add the depression depths to get up to flat:
        self._elev += self._grid.at_node['depression__depth']
        # if apply_slope is none, we're now done! But if not...
        if apply_slope is True:
            apply_slope = DEFAULT_SLOPE
        elif type(apply_slope) in (float, int):
            assert apply_slope >= 0.
        if apply_slope:
            # this isn't very efficient, but OK as we're only running this
            # code ONCE in almost all use cases
            sublake = False
            unstable = True
            stability_increment = 0
            self.lake_nodes_treated = np.array([], dtype=int)
            while unstable:
                while 1:
                    for (outlet_node, lake_code) in zip(self._lf.lake_outlets,
                                                        self._lf.lake_codes):
                        self.apply_slope_current_lake(apply_slope, outlet_node,
                                                      lake_code, sublake)
                    # Call the mapper again here. Bail out if no core pits are
                    # found.
                    # This is necessary as there are some configs where adding
                    # the slope could create subsidiary pits in the topo
                    self._lf.map_depressions(pits=None, reroute_flow=False)
                    if len(self._lf.lake_outlets) == 0.:
                        break
                    self._elev += self._grid.at_node['depression__depth']
                    sublake = True
                    self.lake_nodes_treated = np.array([], dtype=int)
                # final test that all lakes are not reversing flow dirs
                all_lakes = np.where(self._lf.flood_status <
                                     BAD_INDEX_VALUE)[0]
                unstable = self.drainage_directions_change(all_lakes,
                                                           self.original_elev,
                                                           self._elev)
                if unstable:
                    apply_slope *= 0.1
                    sublake = False
                    self.lake_nodes_treated = np.array([], dtype=int)
                    self._elev[:] = original_elev  # put back init conds
                    stability_increment += 1
                    if stability_increment == 10:
                        raise OverflowError('Filler could not find a stable ' +
                                            'condition with a sloping ' +
                                            'surface!')
        # now put back any fields that were present initially, and wipe the
        # rest:
        for delete_me in spurious_fields:
            self._grid.delete_field('node', delete_me)
        for update_me in existing_fields.keys():
            self.grid.at_node[update_me] = existing_fields[update_me]
        # fill the output field
        self.sed_fill_depth[:] = self._elev - self.original_elev

    def add_slopes(self, slope, outlet_node, lake_code):
        """
        Assuming you have already run the lake_mapper, adds an incline towards
        the outlet to the nodes in the lake.
        """
        new_elevs = self._elev.copy()
        outlet_coord = (self._grid.node_x[outlet_node],
                        self._grid.node_y[outlet_node])
        lake_nodes = np.where(self._lf.lake_map == lake_code)[0]
        lake_nodes = np.setdiff1d(lake_nodes, self.lake_nodes_treated)
        lake_ext_margin = self.get_lake_ext_margin(lake_nodes)
        d = self._grid.get_distances_of_nodes_to_point(outlet_coord,
                                                       node_subset=lake_nodes)
        add_vals = slope*d
        new_elevs[lake_nodes] += add_vals
        self.lake_nodes_treated = np.union1d(self.lake_nodes_treated,
                                             lake_nodes)
        return new_elevs, lake_nodes

    def get_lake_ext_margin(self, lake_nodes):
        """
        Returns the nodes forming the external margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        if self._D8 is True:
            all_poss = np.union1d(self._grid.get_neighbor_list(lake_nodes),
                                  self._grid.get_diagonal_list(lake_nodes))
        else:
            all_poss = np.union1d(self._grid.get_neighbor_list(lake_nodes))
        lake_ext_edge = np.setdiff1d(all_poss, lake_nodes)
        return lake_ext_edge[lake_ext_edge != BAD_INDEX_VALUE]

    def get_lake_int_margin(self, lake_nodes, lake_ext_edge):
        """
        Returns the nodes forming the internal margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        lee = lake_ext_edge
        if self._D8 is True:
            all_poss_int = np.union1d(self._grid.get_neighbor_list(lee),
                                      self._grid.get_diagonal_list(lee))
        else:
            all_poss_int = np.union1d(self._grid.get_neighbor_list(lee))
        lake_int_edge = np.intersect1d(all_poss_int, lake_nodes)
        return lake_int_edge[lake_int_edge != BAD_INDEX_VALUE]

    def apply_slope_current_lake(self, apply_slope, outlet_node, lake_code,
                                 sublake):
        """
        Wraps the add_slopes method to allow handling of conditions where the
        drainage structure would be changed or we're dealing with a sublake.
        """
        while 1:
            starting_elevs = self._elev.copy()
            self._elev[:], lake_nodes = self.add_slopes(apply_slope,
                                                        outlet_node, lake_code)
            ext_edge = self.get_lake_ext_margin(lake_nodes)
            if sublake:
                break
            else:
                if not self.drainage_directions_change(lake_nodes,
                                                       starting_elevs,
                                                       self._elev):
                    break
                else:
                    # put the elevs back...
                    self._elev[lake_nodes] = starting_elevs[lake_nodes]
                    # the slope was too big. Reduce it.
                    apply_slope *= 0.1
        # if we get here, either sublake, or drainage dirs are stable

    def drainage_directions_change(self, lake_nodes, old_elevs, new_elevs):
        """
        True if the drainage structure at lake margin changes, False otherwise.
        """
        ext_edge = self.get_lake_ext_margin(lake_nodes)
        print(ext_edge)
        if self._D8:
            edge_neighbors = np.hstack((self._grid.get_neighbor_list(ext_edge),
                                        self._grid.get_diagonal_list(
                                            ext_edge)))
        else:
            edge_neighbors = self._grid.get_neighbor_list(ext_edge).copy()
        edge_neighbors[edge_neighbors == BAD_INDEX_VALUE] = -1
        # ^value irrelevant
        old_neighbor_elevs = old_elevs[edge_neighbors]
        new_neighbor_elevs = new_elevs[edge_neighbors]
        # enforce the "don't change drainage direction" condition:
        edge_elevs = old_elevs[ext_edge].reshape((ext_edge.size, 1))
        cond = np.allclose((edge_elevs >= old_neighbor_elevs),
                           (edge_elevs >= new_neighbor_elevs))
        # if True, we're good, the tilting didn't mess with the fr
        return not cond
<commit_msg>minor bug<commit_after># -*- coding: utf-8 -*-
"""
Created on Mon Oct 19.

@author: dejh
"""
from __future__ import print_function

import landlab
from landlab import ModelParameterDictionary, Component, FieldError, \
                    FIXED_VALUE_BOUNDARY
from landlab.core.model_parameter_dictionary import MissingKeyError
from landlab.components.flow_routing.lake_mapper import \
    DepressionFinderAndRouter
from landlab.components.flow_routing.route_flow_dn import FlowRouter
from landlab.grid.base import BAD_INDEX_VALUE
import numpy as np

DEFAULT_SLOPE = 1.e-5

class SinkFiller(Component):
    """
    This component identifies depressions in a topographic surface, then fills
    them in in the topography.  No attempt is made to conserve sediment mass.
    User may specify whether the holes should be filled to flat, or with a
    slight gradient (~<10**-5) downwards towards the depression outlet.
    """
    _name = 'HoleFiller'

    _input_var_names = set(['topographic__elevation',
                            ])

    _output_var_names = set(['topographic__elevation',
                             'sediment_fill__depth',
                             ])

    _var_units = {'topographic__elevation': 'm',
                  'sediment_fill__depth': 'm',
                  }

    _var_mapping = {'topographic__elevation': 'node',
                    'sediment_fill__depth': 'node',
                    }

    _var_defs = {'topographic__elevation': 'Surface topographic elevation',
                 'sediment_fill__depth': 'Depth of sediment added at each' +
                                         'node',
                 }

    def __init__(self, grid, input_stream=None, current_time=0.,
                 routing='D8'):
        """
        Constructor assigns a copy of the grid, and calls the initialize
        method.

        Parameters
        ----------
        grid : RasterModelGrid
            A landlab RasterModelGrid.
        input_stream : str, file_like, or ModelParameterDictionary, optional
            ModelParameterDictionary that holds the input parameters.
        current_time : float, optional
            The current time for the mapper.
        routing : 'D8' or 'D4' (optional)
            If grid is a raster type, controls whether fill connectivity can
            occur on diagonals ('D8', default), or only orthogonally ('D4').
            Has no effect if grid is not a raster.
        """
        self._grid = grid
        if routing is not 'D8':
            assert routing is 'D4'
        self._routing = routing
        if ((type(self._grid) is landlab.grid.raster.RasterModelGrid) and
                (routing is 'D8')):
            self._D8 = True
            self.num_nbrs = 8
        else:
            self._D8 = False  # useful shorthand for thia test we do a lot
            if type(self._grid) is landlab.grid.raster.RasterModelGrid:
                self.num_nbrs = 4
        self.initialize(input_stream)

    def initialize(self, input_stream=None):
        """
        The BMI-style initialize method takes an optional input_stream
        parameter, which may be either a ModelParameterDictionary object or
        an input stream from which a ModelParameterDictionary can read values.
        """
        # Create a ModelParameterDictionary for the inputs
        if input_stream is None:
            inputs = None
        elif type(input_stream) == ModelParameterDictionary:
            inputs = input_stream
        else:
            inputs = ModelParameterDictionary(input_stream)

        # Make sure the grid includes elevation data. This means either:
        #  1. The grid has a node field called 'topographic__elevation', or
        #  2. The input file has an item called 'ELEVATION_FIELD_NAME' *and*
        #     a field by this name exists in the grid.
        try:
            self._elev = self._grid.at_node['topographic__elevation']
        except FieldError:
            try:
                self.topo_field_name = inputs.read_string('ELEVATION_' +
                                                          'FIELD_NAME')
            except AttributeError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", you need to pass the')
                print('name of a text input file or ModelParameterDictionary,')
                print('and this file or dictionary needs to include the name')
                print('of another field in your grid that contains your')
                print('elevation data.')
                raise AttributeError
            except MissingKeyError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", your input file (or')
                print('ModelParameterDictionary) must include an entry with')
                print('the key "ELEVATION_FIELD_NAME", which gives the name')
                print('of a field in your grid that contains your elevation')
                print('data.')
                raise MissingKeyError('ELEVATION_FIELD_NAME')
            try:
                self._elev = self._grid.at_node[self.topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      self.topo_field_name)
        else:
            self.topo_field_name = 'topographic__elevation'
        # create the only new output field:
        self.sed_fill_depth = self._grid.add_zeros('node',
                                                   'sediment_fill__depth')

        self._lf = DepressionFinderAndRouter(self._grid, routing=self._routing)
        self._fr = FlowRouter(self._grid)

    def fill_pits(self, apply_slope=None):
        """
        This is the main method. Call it to fill depressions in a starting
        topography.

        Parameters
        ----------
        apply_slope : None, bool, or float
            If a float is provided this is the slope of the surface down
            towards the lake outlet. Supply a small positive number, e.g.,
            1.e-5 (or True, to use this default value).
            A test is performed to ensure applying this slope will not alter
            the drainage structure at the edge of the filled region (i.e.,
            that we are not accidentally reversing the flow direction far
            from the outlet.) The component will automatically decrease the
            (supplied or default) gradient a number of times to try to
            accommodate this, but will eventually raise an OverflowError
            if it can't deal with it. If you pass True, the method will use
            the default value of 1.e-5.

        Return fields
        -------------
        'topographic__elevation' : the updated elevations
        'sediment_fill__depth' : the depth of sediment added at each node
        """
        self.original_elev = self._elev.copy()
        # We need this, as we'll have to do ALL this again if we manage
        # to jack the elevs too high in one of the "subsidiary" lakes.
        # We're going to implement the lake_mapper component to do the heavy
        # lifting here, then delete its fields. This means we first need to
        # test if these fields already exist, in which case, we should *not*
        # delete them!
        existing_fields = {}
        spurious_fields = set()
        set_of_outputs = self._lf.output_var_names | self._fr.output_var_names
        try:
            set_of_outputs.remove(self.topo_field_name)
        except KeyError:
            pass
        for field in set_of_outputs:
            try:
                existing_fields[field] = self._grid.at_node[field].copy()
            except FieldError:  # not there; good!
                spurious_fields.add(field)

        self._fr.route_flow(method=self._routing)
        self._lf.map_depressions(pits=self._grid.at_node['flow_sinks'],
                                 reroute_flow=False)
        # add the depression depths to get up to flat:
        self._elev += self._grid.at_node['depression__depth']
        # if apply_slope is none, we're now done! But if not...
        if apply_slope is True:
            apply_slope = DEFAULT_SLOPE
        elif type(apply_slope) in (float, int):
            assert apply_slope >= 0.
        if apply_slope:
            # this isn't very efficient, but OK as we're only running this
            # code ONCE in almost all use cases
            sublake = False
            unstable = True
            stability_increment = 0
            self.lake_nodes_treated = np.array([], dtype=int)
            while unstable:
                while 1:
                    for (outlet_node, lake_code) in zip(self._lf.lake_outlets,
                                                        self._lf.lake_codes):
                        self.apply_slope_current_lake(apply_slope, outlet_node,
                                                      lake_code, sublake)
                    # Call the mapper again here. Bail out if no core pits are
                    # found.
                    # This is necessary as there are some configs where adding
                    # the slope could create subsidiary pits in the topo
                    self._lf.map_depressions(pits=None, reroute_flow=False)
                    if len(self._lf.lake_outlets) == 0.:
                        break
                    self._elev += self._grid.at_node['depression__depth']
                    sublake = True
                    self.lake_nodes_treated = np.array([], dtype=int)
                # final test that all lakes are not reversing flow dirs
                all_lakes = np.where(self._lf.flood_status <
                                     BAD_INDEX_VALUE)[0]
                unstable = self.drainage_directions_change(all_lakes,
                                                           self.original_elev,
                                                           self._elev)
                if unstable:
                    apply_slope *= 0.1
                    sublake = False
                    self.lake_nodes_treated = np.array([], dtype=int)
                    self._elev[:] = original_elev  # put back init conds
                    stability_increment += 1
                    if stability_increment == 10:
                        raise OverflowError('Filler could not find a stable ' +
                                            'condition with a sloping ' +
                                            'surface!')
        # now put back any fields that were present initially, and wipe the
        # rest:
        for delete_me in spurious_fields:
            self._grid.delete_field('node', delete_me)
        for update_me in existing_fields.keys():
            self.grid.at_node[update_me] = existing_fields[update_me]
        # fill the output field
        self.sed_fill_depth[:] = self._elev - self.original_elev

    def add_slopes(self, slope, outlet_node, lake_code):
        """
        Assuming you have already run the lake_mapper, adds an incline towards
        the outlet to the nodes in the lake.
        """
        new_elevs = self._elev.copy()
        outlet_coord = (self._grid.node_x[outlet_node],
                        self._grid.node_y[outlet_node])
        lake_nodes = np.where(self._lf.lake_map == lake_code)[0]
        lake_nodes = np.setdiff1d(lake_nodes, self.lake_nodes_treated)
        lake_ext_margin = self.get_lake_ext_margin(lake_nodes)
        d = self._grid.get_distances_of_nodes_to_point(outlet_coord,
                                                       node_subset=lake_nodes)
        add_vals = slope*d
        new_elevs[lake_nodes] += add_vals
        self.lake_nodes_treated = np.union1d(self.lake_nodes_treated,
                                             lake_nodes)
        return new_elevs, lake_nodes

    def get_lake_ext_margin(self, lake_nodes):
        """
        Returns the nodes forming the external margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        if self._D8 is True:
            all_poss = np.union1d(self._grid.get_neighbor_list(lake_nodes),
                                  self._grid.get_diagonal_list(lake_nodes))
        else:
            all_poss = np.unique(self._grid.get_neighbor_list(lake_nodes))
        lake_ext_edge = np.setdiff1d(all_poss, lake_nodes)
        return lake_ext_edge[lake_ext_edge != BAD_INDEX_VALUE]

    def get_lake_int_margin(self, lake_nodes, lake_ext_edge):
        """
        Returns the nodes forming the internal margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        lee = lake_ext_edge
        if self._D8 is True:
            all_poss_int = np.union1d(self._grid.get_neighbor_list(lee),
                                      self._grid.get_diagonal_list(lee))
        else:
            all_poss_int = np.unique(self._grid.get_neighbor_list(lee))
        lake_int_edge = np.intersect1d(all_poss_int, lake_nodes)
        return lake_int_edge[lake_int_edge != BAD_INDEX_VALUE]

    def apply_slope_current_lake(self, apply_slope, outlet_node, lake_code,
                                 sublake):
        """
        Wraps the add_slopes method to allow handling of conditions where the
        drainage structure would be changed or we're dealing with a sublake.
        """
        while 1:
            starting_elevs = self._elev.copy()
            self._elev[:], lake_nodes = self.add_slopes(apply_slope,
                                                        outlet_node, lake_code)
            ext_edge = self.get_lake_ext_margin(lake_nodes)
            if sublake:
                break
            else:
                if not self.drainage_directions_change(lake_nodes,
                                                       starting_elevs,
                                                       self._elev):
                    break
                else:
                    # put the elevs back...
                    self._elev[lake_nodes] = starting_elevs[lake_nodes]
                    # the slope was too big. Reduce it.
                    apply_slope *= 0.1
        # if we get here, either sublake, or drainage dirs are stable

    def drainage_directions_change(self, lake_nodes, old_elevs, new_elevs):
        """
        True if the drainage structure at lake margin changes, False otherwise.
        """
        ext_edge = self.get_lake_ext_margin(lake_nodes)
        if self._D8:
            edge_neighbors = np.hstack((self._grid.get_neighbor_list(ext_edge),
                                        self._grid.get_diagonal_list(
                                            ext_edge)))
        else:
            edge_neighbors = self._grid.get_neighbor_list(ext_edge).copy()
        edge_neighbors[edge_neighbors == BAD_INDEX_VALUE] = -1
        # ^value irrelevant
        old_neighbor_elevs = old_elevs[edge_neighbors]
        new_neighbor_elevs = new_elevs[edge_neighbors]
        # enforce the "don't change drainage direction" condition:
        edge_elevs = old_elevs[ext_edge].reshape((ext_edge.size, 1))
        cond = np.allclose((edge_elevs >= old_neighbor_elevs),
                           (edge_elevs >= new_neighbor_elevs))
        # if True, we're good, the tilting didn't mess with the fr
        return not cond
<|endoftext|>


<commit_before>"""
spaceshooter.py
Author: will laycock
Credit: me

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
from math import sin, cos, pi, sqrt

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

class BigExplosion(Sprite):

    asset = ImageAsset("images/explosion2.png", Frame(0,0,4800/25,195), 25)

    def __init__(self, position):
        super().__init__(BigExplosion.asset, position)
        self.image = 0
        self.center = (0.5, 0.5)

    def step(self):
        self.setImage(self.image//2)
        self.image = self.image + 1
        if self.image == 50:
            self.destroy()

class Ocean(Sprite):

    asset = ImageAsset("images/toonvectors-83952-140.jpg")

    def __init__(self, position):
        super().__init__(Ocean.asset, position)

class Buoy(Sprite):

    asset = ImageAsset("images/float-clipart-life-buoy-md.png")

    ap = 0

    def __init__(self, position, rotation, next):
        super().__init__(Buoy.asset, position)
        self.prev=None
        self.visible=False
        self.next=next
        if self.next != None:
            self.next.prev=self
        self.rotation = rotation
        self.mass = 30*1000
        self.scale=0.075
        self.fxcenter = 0.5
        self.fycenter = 0.5
        self.circularCollisionModel()
        self.occurab=False
        self.occurbc=False

    def step(self):
        ab=self.collidingWithSprites(Ship)
        bc=self.collidingWithSprites(Ship2)
        if self.visible and ((not self.prev) or (self.prev.occurab and self.prev.occurbc)):
            if len(ab) > 0:
                self.occurab=True
            if len(bc) > 0:
                self.occurbc=True
            if self.occurab and self.occurbc:
                self.visible=False
        if (self.prev==None or self.prev.occurab) and len(ab) and self.next != None:
            self.next.visible=True
        if (self.prev==None or self.prev.occurbc) and len(bc) and self.next != None:
            self.next.visible=True

class Buoy1(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width-150,myapp.height/2), pi/2, next)
        self.visible=True

class Buoy2(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(3/5),myapp.height/4), pi/2, next)

class Buoy3(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(1/4),myapp.height*(4/7)), (2*pi)/3, next)

class Buoy4(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(5/6),myapp.height*(4/7)), (pi)/8, next)

class Ship(Sprite):
    asset = ImageAsset("images/Rivamare-Birds-eye-view-drawing.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.05
        self.vx = 1
        self.vy = 1
        self.vr = 0
        self.rotation=(7*pi)/4
        self.v = 0
        self.thrust = 0
        self.thrustframe = 1
        self.assignkeys()
        self.initposition = position
        self.fxcenter = self.fycenter = 0.5

    def assignkeys(self):
        BoatGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        BoatGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        BoatGame.listenKeyEvent("keydown", "left arrow", self.turnleft)
        BoatGame.listenKeyEvent("keyup", "left arrow", self.turnoff)
        BoatGame.listenKeyEvent("keydown", "right arrow", self.turnright)
        BoatGame.listenKeyEvent("keyup", "right arrow", self.turnoff)

    def step(self):
        vx = cos(self.rotation) * self.v
        vy = -sin(self.rotation) * self.v
        self.x += vx
        self.y += vy
        self.rotation += self.vr
        if self.x > myapp.width:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.x < 0:
            self.x = 65
            self.y=50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y > myapp.height:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y < 0:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.thrust == 0 and self.v >= 0.1:
            self.v -= 0.05
        if self.thrust == 1 and self.v == 0:
            self.v = 1
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
            if self.v < 5:
                self.v += 0.05
        else:
            self.setImage(0)

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

    def turnleft(self, event):
        self.vr = 0.05

    def turnoff(self, event):
        self.vr = 0

    def turnright(self, event):
        self.vr = -0.05

class Ship2(Ship):
    asset = ImageAsset("images/boat10.png")

    def assignkeys(self):
        self.scale=0.075
        BoatGame.listenKeyEvent("keydown", "w", self.thrustOn)
        BoatGame.listenKeyEvent("keyup", "w", self.thrustOff)
        BoatGame.listenKeyEvent("keydown", "a", self.turnleft)
        BoatGame.listenKeyEvent("keyup", "a", self.turnoff)
        BoatGame.listenKeyEvent("keydown", "d", self.turnright)
        BoatGame.listenKeyEvent("keyup", "d", self.turnoff)

    def step(self):
        vx = cos(self.rotation) * self.v
        vy = -sin(self.rotation) * self.v
        self.x += vx
        self.y += vy
        self.rotation += self.vr
        if self.x > myapp.width:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.x < 0:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y > myapp.height:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y < 0:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.thrust == 0 and self.v >= 0.1:
            self.v -= 0.05
        if self.thrust == 1 and self.v == 0:
            self.v = 1
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
            if self.v < 5:
                self.v += 0.05
        else:
            self.setImage(0)

class tally1(Sprite):
    asset = ImageAsset("images/Tally1.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally2(Sprite):
    asset = ImageAsset("images/Tally2.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally3(Sprite):
    asset = ImageAsset("images/Tally3.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally4(Sprite):
    asset = ImageAsset("images/Tally4.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally5(Sprite):
    asset = ImageAsset("images/Tally5.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class BoatGame(App):
    def __init__(self, width, height):
        super().__init__()
        ocean = Ocean((0,0))
        tal = tally5((self.width-50, 30))
        ocean.scale = self.width/ocean.width
        self.ss = Ship((65,50))
        self.sv= Ship2((65-sqrt(300),50+sqrt(300)))

    def step(self):
        for ship in self.getSpritesbyClass(Ship):
            ship.step()
        for ship in self.getSpritesbyClass(Ship2):
            ship.step()
        for exp in self.getSpritesbyClass(BigExplosion):
            exp.step()

        b1.step()
        b2.step()
        b3.step()
        b4.step()

myapp = BoatGame(SCREEN_WIDTH, SCREEN_HEIGHT)
b4 = Buoy4(None)
b3 = Buoy3(b4)
b2 = Buoy2(b3)
b1 = Buoy1(b2)
myapp.run()
<commit_msg>Updated from Brython Server: 6/9/2017 2:55:56 PM<commit_after>"""
spaceshooter.py
Author: will laycock
Credit: me

Assignment:
Write and submit a program that implements the spacewar game:
https://github.com/HHS-IntroProgramming/Spacewar
"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
from math import sin, cos, pi, sqrt

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

class BigExplosion(Sprite):

    asset = ImageAsset("images/explosion2.png", Frame(0,0,4800/25,195), 25)

    def __init__(self, position):
        super().__init__(BigExplosion.asset, position)
        self.image = 0
        self.center = (0.5, 0.5)

    def step(self):
        self.setImage(self.image//2)
        self.image = self.image + 1
        if self.image == 50:
            self.destroy()

class Ocean(Sprite):

    asset = ImageAsset("images/toonvectors-83952-140.jpg")

    def __init__(self, position):
        super().__init__(Ocean.asset, position)

class Buoy(Sprite):

    asset = ImageAsset("images/float-clipart-life-buoy-md.png")

    ap = 0

    def __init__(self, position, rotation, next):
        super().__init__(Buoy.asset, position)
        self.prev=None
        self.visible=False
        self.next=next
        if self.next != None:
            self.next.prev=self
        self.rotation = rotation
        self.mass = 30*1000
        self.scale=0.075
        self.fxcenter = 0.5
        self.fycenter = 0.5
        self.circularCollisionModel()
        self.occurab=False
        self.occurbc=False

    def step(self):
        ab=self.collidingWithSprites(Ship)
        bc=self.collidingWithSprites(Ship2)
        if self.visible and ((not self.prev) or self.prev.occurab):
            if len(ab) > 0:
                self.occurab=True
        if self.visible and ((not self.prev) or self.prev.occurbc):
            if len(bc) > 0:
                self.occurbc=True
        if self.occurab and self.occurbc:
            self.visible=False
        if (self.prev==None or self.prev.occurab) and len(ab) and self.next != None:
            self.next.visible=True
        if (self.prev==None or self.prev.occurbc) and len(bc) and self.next != None:
            self.next.visible=True

class Buoy1(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width-150,myapp.height/2), pi/2, next)
        self.visible=True

class Buoy2(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(3/5),myapp.height/4), pi/2, next)

class Buoy3(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(1/4),myapp.height*(4/7)), (2*pi)/3, next)

class Buoy4(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(5/6),myapp.height*(6/7)), (pi)/8, next)

class Buoy5(Buoy):
    def __init__(self, next):
        super().__init__((myapp.width*(2/7),myapp.height*(6/7)), (5*pi)/6, next)

class Ship(Sprite):
    asset = ImageAsset("images/Rivamare-Birds-eye-view-drawing.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.05
        self.vx = 1
        self.vy = 1
        self.vr = 0
        self.rotation=(7*pi)/4
        self.v = 0
        self.thrust = 0
        self.thrustframe = 1
        self.assignkeys()
        self.initposition = position
        self.fxcenter = self.fycenter = 0.5
        """self.buoy1l=0
        self.buoy2l=0
        self.buoy3l=0
        self.buoy4l=0
        self.buoy5l=0
        self.buoy1s=0
        self.buoy2s=0
        self.buoy3s=0
        self.buoy4s=0
        self.buoy5s=0"""

    def assignkeys(self):
        BoatGame.listenKeyEvent("keydown", "up arrow", self.thrustOn)
        BoatGame.listenKeyEvent("keyup", "up arrow", self.thrustOff)
        BoatGame.listenKeyEvent("keydown", "left arrow", self.turnleft)
        BoatGame.listenKeyEvent("keyup", "left arrow", self.turnoff)
        BoatGame.listenKeyEvent("keydown", "right arrow", self.turnright)
        BoatGame.listenKeyEvent("keyup", "right arrow", self.turnoff)

    def step(self):
        vx = cos(self.rotation) * self.v
        vy = -sin(self.rotation) * self.v
        self.x += vx
        self.y += vy
        self.rotation += self.vr
        if self.x > myapp.width:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.x < 0:
            self.x = 65
            self.y=50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y > myapp.height:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y < 0:
            self.x = 65
            self.y = 50
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.thrust == 0 and self.v >= 0.1:
            self.v -= 0.05
        if self.thrust == 1 and self.v == 0:
            self.v = 1
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
            if self.v < 5:
                self.v += 0.05
        else:
            self.setImage(0)
    """self.buoy1l=self.collidingWithSprites(Buoy1)
    self.buoy2l=self.collidingWithSprites(Buoy2)
    self.buoy3l=self.collidingWithSprites(Buoy3)
    self.buoy4l=self.collidingWithSprites(Buoy4)
    self.buoy5l=self.collidingWithSprites(Buoy5)
    if len(self.buoy5l)>0:
        self.buoy5s=1
    if len(self.buoy1l)>0:
        self.buoy1s=1
    if len(self.buoy2l)>0:
        self.buoy2s=1
    if len(self.buoy3l)>0:
        self.buoy3s=1
    if len(self.buoy4l)>0:
        self.buoy4s=1
    if len(self.buoy5l)>0:
        self.buoy5s=1
    if self.buoy5s==1 and self.buoy1s==1 and self.buoy2s==1 and self.buoy3s==1 and self.buoy4s==1:
        print("kk")
        ship1wins()"""

    def thrustOn(self, event):
        self.thrust = 1

    def thrustOff(self, event):
        self.thrust = 0

    def turnleft(self, event):
        self.vr = 0.05

    def turnoff(self, event):
        self.vr = 0

    def turnright(self, event):
        self.vr = -0.05

class Ship2(Ship):
    asset = ImageAsset("images/boat10.png")

    def assignkeys(self):
        self.scale=0.075
        BoatGame.listenKeyEvent("keydown", "w", self.thrustOn)
        BoatGame.listenKeyEvent("keyup", "w", self.thrustOff)
        BoatGame.listenKeyEvent("keydown", "a", self.turnleft)
        BoatGame.listenKeyEvent("keyup", "a", self.turnoff)
        BoatGame.listenKeyEvent("keydown", "d", self.turnright)
        BoatGame.listenKeyEvent("keyup", "d", self.turnoff)

    def step(self):
        vx = cos(self.rotation) * self.v
        vy = -sin(self.rotation) * self.v
        self.x += vx
        self.y += vy
        self.rotation += self.vr
        if self.x > myapp.width:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.x < 0:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y > myapp.height:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.y < 0:
            self.x = 65-sqrt(300)
            self.y = 50+sqrt(300)
            self.v = 0
            self.thrust = 0
            self.rotation = (7*pi)/4
        if self.thrust == 0 and self.v >= 0.1:
            self.v -= 0.05
        if self.thrust == 1 and self.v == 0:
            self.v = 1
        if self.thrust == 1:
            self.setImage(self.thrustframe)
            self.thrustframe += 1
            if self.thrustframe == 4:
                self.thrustframe = 1
            if self.v < 5:
                self.v += 0.05
        else:
            self.setImage(0)
    """def ship1wins(self):
        self.visbile=False"""

class tally1(Sprite):
    asset = ImageAsset("images/Tally1.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally2(Sprite):
    asset = ImageAsset("images/Tally2.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally3(Sprite):
    asset = ImageAsset("images/Tally3.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally4(Sprite):
    asset = ImageAsset("images/Tally4.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class tally5(Sprite):
    asset = ImageAsset("images/Tally5.png")

    def __init__(self, position):
        super().__init__(self.asset, position)
        self.scale=0.075
        self.fxcenter = self.fycenter = 0.5

class BoatGame(App):
    def __init__(self, width, height):
        super().__init__()
        ocean = Ocean((0,0))
        tal = tally5((self.width-50, 30))
        ocean.scale = self.width/ocean.width
        self.ss = Ship((65,50))
        self.sv= Ship2((65-sqrt(300),50+sqrt(300)))

    def step(self):
        for ship in self.getSpritesbyClass(Ship):
            ship.step()
        for ship in self.getSpritesbyClass(Ship2):
            ship.step()
        for exp in self.getSpritesbyClass(BigExplosion):
            exp.step()

        b1.step()
        b2.step()
        b3.step()
        b4.step()
        b5.step()

myapp = BoatGame(SCREEN_WIDTH, SCREEN_HEIGHT)
b5 = Buoy5(None)
b4 = Buoy4(b5)
b3 = Buoy3(b4)
b2 = Buoy2(b3)
b1 = Buoy1(b2)
myapp.run()
<|endoftext|>


<commit_before>import zmq

info_port = 5559

def main():
	with zmq.Context() as context:
		with context.socket(zmq.SUB) as socket:
		    socket.setsockopt_string(zmq.SUBSCRIBE, "")
		    address = "tcp://localhost:{}".format(info_port)
		    socket.connect(address)
		    while True:
		        msg = socket.recv_json()
		        print(msg)

if __name__ == '__main__':
    main()
<commit_msg>soft tabs<commit_after>import zmq

info_port = 5559

def main():
    with zmq.Context() as context:
        with context.socket(zmq.SUB) as socket:
            socket.setsockopt_string(zmq.SUBSCRIBE, "")
            address = "tcp://localhost:{}".format(info_port)
            socket.connect(address)
            while True:
                msg = socket.recv_json()
                print(msg)

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


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

import webnsock
import web
from signal import signal, SIGINT
from os import path
from logging import error, warn, info, debug, basicConfig, INFO

from csv import DictWriter
from datetime import datetime
from time import mktime
from threading import Lock
from collections import defaultdict

basicConfig(level=INFO)

class CARState:

    def __init__(self):
        self.states = {}
        self.clients = set([])
        self.admin_clients = set([])
        self.users = {}
        self.gps = defaultdict(dict)

        for u in self.users:
            self.set_state(u, 'INIT')
        self.log_filename = \
            'call-a-robot-' + datetime.now().strftime("%Y%m%d-%H%M%S") + '.csv'
        self.csvfile = open(self.log_filename, 'w', 0)
        self.log_fieldnames = [
            'id', 'timestamp', 'datetime', 'user', 'log_user', 'uid',
            'state', 'latitude', 'longitude']
        self.log_writer = DictWriter(
            self.csvfile, fieldnames=self.log_fieldnames
        )
        self.log_writer.writeheader()
        self.log_id = 0
        self.log_lock = Lock()

        self.log_uid = defaultdict(int)

    def log(self, user, state='NONE', log_user="", latitude=-1, longitude=-1):
        dt = datetime.now()
        ts = mktime(dt.timetuple())

        if 'latitude' in self.gps[user]:
            latitude = self.gps[user]['latitude']
        if 'longitude' in self.gps[user]:
            longitude = self.gps[user]['longitude']

        entry = {
            'id': self.log_id,
            'timestamp': int(ts),
            'datetime': dt.strftime("%Y%m%d-%H%M%S"),
            'user': user,
            'log_user': log_user,
            'uid': self.log_uid[user],
            'state': state,
            'latitude': latitude,
            'longitude': longitude
        }

        with self.log_lock:
            self.log_writer.writerow(entry)
        if state is not 'GPS':
            for a in self.admin_clients:
                a.sendJSON({
                    'method': 'add_log',
                    'id': self.log_id,
                    'timestamp': int(ts),
                    'datetime': dt.strftime("%Y%m%d-%H%M%S"),
                    'user': user,
                    'log_user': log_user,
                    'uid': self.log_uid[user],
                    'state': state,
                    'latitude': latitude,
                    'longitude': longitude
                })
        self.log_id += 1
        if state == 'INIT' or state == 'INIT':
            self.log_uid[user] += 1

    def get_state(self, user):
        if user not in self.states:
            self.states[user] = 'INIT'
        return self.states[user]

    def set_state(self, user, state, log_user, latitude=-1, longitude=-1):
        self.states[user] = state
        self.log(user, state, log_user, latitude, longitude)

    def send_updated_states(self, extra_socket=None):
        if extra_socket is None:
            addressees = set([])
        else:
            addressees = set([extra_socket])
        addressees = addressees.union(self.clients)
        for m in addressees:
            info('send update to manager %s' % str(m))
            m.sendJSON({
                'method': 'update_orders',
                'states': self.states
            })

    def send_update_position(self, user, lat, long):
        for m in self.admin_clients:
            info('send pos update  %s' % str(m))
            m.sendJSON({
                'method': 'update_position',
                'user': user,
                'lat': lat,
                'long': long
            })

car_states = CARState()

class CARWebServer(webnsock.WebServer):

    def get_text(self, text):
        return text

    def __init__(self):

        self.car_states = car_states

        self.params = {
            'n_users': len(self.car_states.users),
            'users': list(self.car_states.users)
        }

        webnsock.WebServer.__init__(
            self,
            path.join(
                path.dirname(__file__),
                'www/static'
            ),
            '/car/'
        )

        self._renderer = web.template.render(
            path.realpath(
                path.join(
                    path.dirname(__file__),
                    'www'
                )
            ),
            base='base', globals=globals())

        self_app = self

        class AMZBtn(self.page):
            path = '/car/button'

            def POST(self):
                user_data = web.input(username='')
                info('pressed the button')
                user = str(user_data.username)
                if user is not '':
                    self_app.car_states.users[user] = web.ctx
                    self_app.params = {
                        'n_users': len(self_app.car_states.users),
                        'users': list(self_app.car_states.users)
                    }

                    info('user %s pressed the button')
                    self_app.car_states.set_state(user, 'BUTTON')
                    self_app.car_states.send_updated_states()
                return web.ok()

        class Download(self.page):
            path = '/car/log'

            def GET(self):
                web.header(
                    'Content-Disposition', 'attachment; filename="car.log"')
                web.header('Content-type', 'images/jpeg')
                web.header('Content-transfer-encoding', 'binary')
                return open(self_app.car_states.log_filename, 'rb').read()

        class Index(self.page):
            path = '/car/'

            def GET(self):
                user = web.cookies().get('_car_user')
                if user is None:
                    return self_app._renderer.login(
                        self_app.params, self_app.get_text, '/car/')
                else:
                    self_app.car_states.users[user] = web.ctx
                    self_app.params = {
                        'n_users': len(self_app.car_states.users),
                        'users': list(self_app.car_states.users)
                    }

                    return self_app._renderer.index(
                        self_app.params, self_app.get_text, user)

            def POST(self):
                user_data = web.input(username='')

                user = user_data.username
                if user is not '':
                    info('login as %s' % user_data)
                    web.setcookie('_car_user', user)
                else:
                    web.setcookie('_car_user', '', -1)
                return web.seeother('/car/')

        class Orders(self.page):
            path = '/car/orders'

            def POST(self):
                user_data = web.input(username='')

                user = user_data.username
                if user == 'lcas':
                    info('admin login as %s' % user_data)
                    web.setcookie('_car_admin', user)
                else:
                    web.setcookie('_car_admin', '', -1)
                return web.seeother('/car/orders')

            def GET(self):
                user = web.cookies().get('_car_admin')
                if user is None:
                    return self_app._renderer.login(
                        self_app.params, self_app.get_text, '/car/orders')
                else:
                    return self_app._renderer.orders(
                        self_app.params, self_app.get_text)

class CARProtocol(webnsock.JsonWSProtocol):

    def __init__(self):
        super(CARProtocol, self).__init__()
        self.car_states = car_states
        self.log_user = 'unknown'

    def onOpen(self):
        info('websocket opened')
        self.car_states.clients.add(self)

    def onClose(self, wasClean, code, reason):
        info("WebSocket connection closed: {0}".format(reason))
        if self in self.car_states.clients:
            info('unregistered')
            self.car_states.clients.remove(self)

    def on_set_state(self, payload):
        info(
            'update state for user %s: %s' %
            (payload['user'], payload['state']))
        self.update_state(payload['user'], payload['state'])

    def on_register(self, payload):
        info('registering management interface %s' % str(self))
        if payload['admin']:
            self.car_states.admin_clients.add(self)
        self.log_user = payload['user']

    def on_get_states(self, payload):
        info('states requested')
        self.send_updated_states()

    def on_location_update(self, payload):
        info('GPS update')
        self.car_states.gps[payload['user']] = {
            'latitude': payload['latitude'],
            'longitude': payload['longitude']
        }
        self.car_states.log(
            payload['user'],
            state='GPS',
            latitude=payload['latitude'],
            longitude=payload['longitude']
        )
        self.car_states.send_update_position(
            payload['user'],
            payload['latitude'],
            payload['longitude'])

    def send_updated_states(self):
        self.car_states.send_updated_states(self)

    def update_state(self, user, state):
        self.car_states.set_state(user, state, self.log_user)
        self.send_updated_states()

    def on_ping(self, payload):
        return {'result': True}

    def on_call(self, payload):
        info('user %s called a robot' % payload['user'])
        self.update_state(payload['user'], 'CALLED')

    def on_cancel(self, payload):
        info('user %s cancelled a robot' % payload['user'])
        self.update_state(payload['user'], 'INIT')

    def on_get_state(self, payload):
        info('user %s requested state' % payload['user'])
        return {
            'method': 'set_state',
            'state': self.car_states.get_state(payload['user'])
        }

def main():
    webserver = webnsock.WebserverThread(CARWebServer())
    backend = webnsock.WSBackend(CARProtocol)
    signal(SIGINT,
           lambda s, f: webnsock.signal_handler(webserver, backend, s, f))
    webserver.start()
    backend.talker()

if __name__ == "__main__":
    main()
<commit_msg>fixed set_state<commit_after>#!/usr/bin/env python

import webnsock
import web
from signal import signal, SIGINT
from os import path
from logging import error, warn, info, debug, basicConfig, INFO

from csv import DictWriter
from datetime import datetime
from time import mktime
from threading import Lock
from collections import defaultdict

basicConfig(level=INFO)

class CARState:

    def __init__(self):
        self.states = {}
        self.clients = set([])
        self.admin_clients = set([])
        self.users = {}
        self.gps = defaultdict(dict)

        for u in self.users:
            self.set_state(u, 'INIT')
        self.log_filename = \
            'call-a-robot-' + datetime.now().strftime("%Y%m%d-%H%M%S") + '.csv'
        self.csvfile = open(self.log_filename, 'w', 0)
        self.log_fieldnames = [
            'id', 'timestamp', 'datetime', 'user', 'log_user', 'uid',
            'state', 'latitude', 'longitude']
        self.log_writer = DictWriter(
            self.csvfile, fieldnames=self.log_fieldnames
        )
        self.log_writer.writeheader()
        self.log_id = 0
        self.log_lock = Lock()

        self.log_uid = defaultdict(int)

    def log(self, user, state='NONE', log_user="", latitude=-1, longitude=-1):
        dt = datetime.now()
        ts = mktime(dt.timetuple())

        if 'latitude' in self.gps[user]:
            latitude = self.gps[user]['latitude']
        if 'longitude' in self.gps[user]:
            longitude = self.gps[user]['longitude']

        entry = {
            'id': self.log_id,
            'timestamp': int(ts),
            'datetime': dt.strftime("%Y%m%d-%H%M%S"),
            'user': user,
            'log_user': log_user,
            'uid': self.log_uid[user],
            'state': state,
            'latitude': latitude,
            'longitude': longitude
        }

        with self.log_lock:
            self.log_writer.writerow(entry)
        if state is not 'GPS':
            for a in self.admin_clients:
                a.sendJSON({
                    'method': 'add_log',
                    'id': self.log_id,
                    'timestamp': int(ts),
                    'datetime': dt.strftime("%Y%m%d-%H%M%S"),
                    'user': user,
                    'log_user': log_user,
                    'uid': self.log_uid[user],
                    'state': state,
                    'latitude': latitude,
                    'longitude': longitude
                })
        self.log_id += 1
        if state == 'INIT' or state == 'INIT':
            self.log_uid[user] += 1

    def get_state(self, user):
        if user not in self.states:
            self.states[user] = 'INIT'
        return self.states[user]

    def set_state(self, user, state, log_user="", latitude=-1, longitude=-1):
        self.states[user] = state
        self.log(user, state, log_user, latitude, longitude)

    def send_updated_states(self, extra_socket=None):
        if extra_socket is None:
            addressees = set([])
        else:
            addressees = set([extra_socket])
        addressees = addressees.union(self.clients)
        for m in addressees:
            info('send update to manager %s' % str(m))
            m.sendJSON({
                'method': 'update_orders',
                'states': self.states
            })

    def send_update_position(self, user, lat, long):
        for m in self.admin_clients:
            info('send pos update  %s' % str(m))
            m.sendJSON({
                'method': 'update_position',
                'user': user,
                'lat': lat,
                'long': long
            })

car_states = CARState()

class CARWebServer(webnsock.WebServer):

    def get_text(self, text):
        return text

    def __init__(self):

        self.car_states = car_states

        self.params = {
            'n_users': len(self.car_states.users),
            'users': list(self.car_states.users)
        }

        webnsock.WebServer.__init__(
            self,
            path.join(
                path.dirname(__file__),
                'www/static'
            ),
            '/car/'
        )

        self._renderer = web.template.render(
            path.realpath(
                path.join(
                    path.dirname(__file__),
                    'www'
                )
            ),
            base='base', globals=globals())

        self_app = self

        class AMZBtn(self.page):
            path = '/car/button'

            def POST(self):
                user_data = web.input(username='')
                info('pressed the button')
                user = str(user_data.username)
                if user is not '':
                    self_app.car_states.users[user] = web.ctx
                    self_app.params = {
                        'n_users': len(self_app.car_states.users),
                        'users': list(self_app.car_states.users)
                    }

                    info('user %s pressed the button')
                    self_app.car_states.set_state(user, 'BUTTON')
                    self_app.car_states.send_updated_states()
                return web.ok()

        class Download(self.page):
            path = '/car/log'

            def GET(self):
                web.header(
                    'Content-Disposition', 'attachment; filename="car.log"')
                web.header('Content-type', 'images/jpeg')
                web.header('Content-transfer-encoding', 'binary')
                return open(self_app.car_states.log_filename, 'rb').read()

        class Index(self.page):
            path = '/car/'

            def GET(self):
                user = web.cookies().get('_car_user')
                if user is None:
                    return self_app._renderer.login(
                        self_app.params, self_app.get_text, '/car/')
                else:
                    self_app.car_states.users[user] = web.ctx
                    self_app.params = {
                        'n_users': len(self_app.car_states.users),
                        'users': list(self_app.car_states.users)
                    }

                    return self_app._renderer.index(
                        self_app.params, self_app.get_text, user)

            def POST(self):
                user_data = web.input(username='')

                user = user_data.username
                if user is not '':
                    info('login as %s' % user_data)
                    web.setcookie('_car_user', user)
                else:
                    web.setcookie('_car_user', '', -1)
                return web.seeother('/car/')

        class Orders(self.page):
            path = '/car/orders'

            def POST(self):
                user_data = web.input(username='')

                user = user_data.username
                if user == 'lcas':
                    info('admin login as %s' % user_data)
                    web.setcookie('_car_admin', user)
                else:
                    web.setcookie('_car_admin', '', -1)
                return web.seeother('/car/orders')

            def GET(self):
                user = web.cookies().get('_car_admin')
                if user is None:
                    return self_app._renderer.login(
                        self_app.params, self_app.get_text, '/car/orders')
                else:
                    return self_app._renderer.orders(
                        self_app.params, self_app.get_text)

class CARProtocol(webnsock.JsonWSProtocol):

    def __init__(self):
        super(CARProtocol, self).__init__()
        self.car_states = car_states
        self.log_user = 'unknown'

    def onOpen(self):
        info('websocket opened')
        self.car_states.clients.add(self)

    def onClose(self, wasClean, code, reason):
        info("WebSocket connection closed: {0}".format(reason))
        if self in self.car_states.clients:
            info('unregistered')
            self.car_states.clients.remove(self)

    def on_set_state(self, payload):
        info(
            'update state for user %s: %s' %
            (payload['user'], payload['state']))
        self.update_state(payload['user'], payload['state'])

    def on_register(self, payload):
        info('registering management interface %s' % str(self))
        if payload['admin']:
            self.car_states.admin_clients.add(self)
        self.log_user = payload['user']

    def on_get_states(self, payload):
        info('states requested')
        self.send_updated_states()

    def on_location_update(self, payload):
        info('GPS update')
        self.car_states.gps[payload['user']] = {
            'latitude': payload['latitude'],
            'longitude': payload['longitude']
        }
        self.car_states.log(
            payload['user'],
            state='GPS',
            latitude=payload['latitude'],
            longitude=payload['longitude']
        )
        self.car_states.send_update_position(
            payload['user'],
            payload['latitude'],
            payload['longitude'])

    def send_updated_states(self):
        self.car_states.send_updated_states(self)

    def update_state(self, user, state):
        self.car_states.set_state(user, state, self.log_user)
        self.send_updated_states()

    def on_ping(self, payload):
        return {'result': True}

    def on_call(self, payload):
        info('user %s called a robot' % payload['user'])
        self.update_state(payload['user'], 'CALLED')

    def on_cancel(self, payload):
        info('user %s cancelled a robot' % payload['user'])
        self.update_state(payload['user'], 'INIT')

    def on_get_state(self, payload):
        info('user %s requested state' % payload['user'])
        return {
            'method': 'set_state',
            'state': self.car_states.get_state(payload['user'])
        }

def main():
    webserver = webnsock.WebserverThread(CARWebServer())
    backend = webnsock.WSBackend(CARProtocol)
    signal(SIGINT,
           lambda s, f: webnsock.signal_handler(webserver, backend, s, f))
    webserver.start()
    backend.talker()

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


<commit_before>#
# Copyright (c) rPath, Inc.
#

'''
Build, commit, and process troves in "batches"
'''

import logging
import os
import sys
import time

from conary.build.macros import Macros
from rmake.cmdline import monitor

from bob import commit
from bob import flavors
from bob import test
from bob.errors import JobFailedError, TestFailureError
from bob.util import ContextCache, StatusOnlyDisplay
from bob.util import partial, pushStopHandler, popStopHandler

log = logging.getLogger('bob.cook')

def stopJob(batch, signum, _):
    '''
    Signal handler used during a cook job that will stop the job
    and exit.
    '''

    log.error('Caught signal %d during build; stopping job', signum)
    batch.stop()
    sys.exit('Signalled stop')

class Batch(object):
    '''
    A batch of troves to be built at once; e.g. packages or groups. A
    batch may contain either troves or groups but not both, and if it
    includes groups, none of the groups may add another group also in
    the same batch.
    '''

    def __init__(self, clientHelper):
        self._helper = clientHelper

        # setup
        self._contextCache = ContextCache(self._helper.cfg)
        self._bobTroves = []
        self._troves = set()
        self._commit = None

        # job state
        self._jobId = None

        # results
        self._testSuite = None
        self._coverageData = None

    def isEmpty(self):
        """
        Returns C{True} if there are no troves in this job.
        """
        return not self._troves

    def addTrove(self, bobTrove):
        '''
        Add a I{BobPackage} to the batch.

        @type bobTrove: L{bob.trove.BobPackage}
        '''

        _macros = Macros(self._helper.plan.macros)
        macros = {}
        config = bobTrove.getTargetConfig()

        doCommit = not config.noCommit
        if self._commit is None:
            self._commit = doCommit
        elif self._commit != doCommit:
            senseA = doCommit and "wants" or "does not want"
            senseB = doCommit and "do not" or "do"
            log.error("Package %s %s to commit, but existing packages "
                "in batch %s", bobTrove.getPackageName(), senseA, senseB)
            log.error("Either all packages in a batch must commit, or none can")
            raise RuntimeError("Can't commit part of a batch")

        if config:
            for key, value in config.macros.iteritems():
                if key not in self._helper.plan.skipMacros:
                    macros[key] = value % _macros

        # Reduce the set of flavors to build
        oldFlavors = bobTrove.getFlavors()
        newFlavors = flavors.reduce_flavors(bobTrove.getPackageName(),
            config, oldFlavors)

        if len(newFlavors) != len(oldFlavors):
            log.debug('Package %s would be built in %d flavors; '
                'now built in %d flavors', bobTrove.getPackageName(),
                len(oldFlavors), len(newFlavors))
        else:
            log.debug('Package %s will be built in %d flavors',
                bobTrove.getPackageName(), len(newFlavors))

        for buildFlavor in newFlavors:
            # Calculate build parameters
            searchFlavors = flavors.guess_search_flavors(buildFlavor)

            # Get a context in which to build the trove
            context = self._contextCache.get(buildFlavor, searchFlavors,
                macros)

            # Add the tuple to the build list
            self._bobTroves.append(bobTrove)
            self._troves.add((bobTrove.getName(),
                bobTrove.getDownstreamVersion(), buildFlavor, context))

    def run(self, main):
        '''
        Create and run a rMake build job from the set of added troves,
        process the resulting artifacts, and commit if no errors or
        failed tests were encountered.
        '''

        troveNames = sorted(set(x[0].split(':')[0] for x in self._troves))
        log.info('Creating build job: %s', ' '.join(troveNames))

        # Create rMake job
        job = self._helper.getrMakeHelper().createBuildJob(list(self._troves),
                buildConfig=self._helper.cfg)
        jobId = self._helper.getrMakeClient().buildJob(job)
        log.info('Job %d started with these sources:', jobId)
        for trove in self._bobTroves:
            version = trove.getDownstreamVersion()
            log.info(' %s=%s/%s', trove.getName(),
                    version.trailingLabel(), version.trailingRevision())

        # Set a signal handler so we can stop the job if we get
        # interrupted
        self._jobId = jobId
        pushStopHandler(partial(stopJob, self))

        # Watch build (to stdout)
        self._helper.callClientHook('client_preCommand', main,
            None, (self._helper.cfg, self._helper.cfg),
            None, None)
        self._helper.callClientHook('client_preCommand2', main,
            self._helper.getrMakeHelper(), None)
        monitor.monitorJob(self._helper.getrMakeClient(), jobId,
            exitOnFinish=True, displayClass=StatusOnlyDisplay,
            showBuildLogs=self._helper.plan.showBuildLogs)

        # Remove the signal handler now that the job is done
        self._jobId = None
        popStopHandler()

        # Pull out logs
        job = self._helper.getrMakeClient().getJob(jobId)
        self.writeLogs(job)

        # Check for error condition
        if job.isFailed():
            log.error('Job %d failed', jobId)
            raise JobFailedError(jobId=jobId, why='Job failed')
        elif not job.isFinished():
            log.error('Job %d is not done, yet watch returned early!', jobId)
            raise JobFailedError(jobId=jobId, why='Job not done')
        elif not list(job.iterBuiltTroves()):
            log.error('Job %d has no built troves', jobId)
            raise JobFailedError(jobId=jobId, why='Job built no troves')

        # Fetch test/coverage output and report results
        self._testSuite, self._coverageData = test.processTests(self._helper,
            job)
        print 'Batch results:', self._testSuite.describe()

        # Bail out without committing if tests failed ...
        if not self._testSuite.isSuccessful():
            log.error('Some tests failed, aborting')
            raise TestFailureError()

        # ... or if all packages are set not to commit
        if self._commit is False:
            return {}

        # Commit to target repository
        if job.isCommitting():
            log.error('Job %d is already committing ' \
                '(probably to the wrong place)', jobId)
            raise JobFailedError(jobId=jobId, why='Job already committing')

        startTime = time.time()
        log.info('Starting commit of job %d', jobId)
        self._helper.getrMakeClient().startCommit([jobId])

        try:
            mapping = commit.commit(self._helper, job)
        except Exception, e_value:
            self._helper.getrMakeClient().commitFailed([jobId], str(e_value))
            raise
        else:
            self._helper.getrMakeClient().commitSucceeded(mapping)
            log.info('Commit of job %d completed in %.02f seconds',
                jobId, time.time() - startTime)
        return mapping

    def stop(self):
        '''
        Stop the currently running build.
        '''
        if self._jobId:
            self._helper.getrMakeHelper().stopJob(self._jobId)

    def getTestSuite(self):
        '''
        Retrieve testsuite data compiled after a batch is run.

        @rtype: L{TestSuite<bob.test.TestSuite>}
        '''
        return self._testSuite

    def getCoverageData(self):
        '''
        Retrieve coverage data compiled after a batch is run.

        @rtype: C{dict([(filename,
                (set([statements]), set([missing])))])}
        '''
        return self._coverageData

    def writeLogs(self, job):
        """
        Write build logs for job C{job} to the output directory.
        """
        jobDir = os.path.join('output', 'logs', str(job.jobId))
        client = self._helper.getrMakeClient()
        for trv in job.iterTroves():
            troveName = '%s{%s}' % (trv.getName(), trv.getContext())
            troveDir = os.path.join(jobDir, troveName)
            if not os.path.isdir(troveDir):
                os.makedirs(troveDir)

            toStdout = False
            prefix = '[%s] ' % troveName
            if trv.isFailed():
                toStdout = True
                print >> sys.stderr, 'Trove %s failed to build:' % troveName
                sys.stderr.flush()

            troveLog = open(os.path.join(troveDir, 'trove.log'), 'w')
            mark = 0
            while True:
                logs = client.getTroveLogs(job.jobId,
                    trv.getNameVersionFlavor(True), mark)
                if not logs:
                    break
                mark += len(logs)

                for timeStamp, message, _ in logs:
                    for line in message.splitlines():
                        line = '[%s] %s\n' % (timeStamp, line.rstrip())
                        troveLog.write(line)
                        if toStdout:
                            sys.stdout.write(prefix + line)
            troveLog.close()

            buildLog = open(os.path.join(troveDir, 'build.log'), 'w')
            mark = 0
            while True:
                _, logs, mark = client.getTroveBuildLog(job.jobId,
                    trv.getNameVersionFlavor(True), mark)
                if not logs:
                    break
                mark += len(logs)
                buildLog.write(logs)
                if toStdout:
                    for line in logs.splitlines():
                        print prefix + line.rstrip()
            buildLog.close()

            failureReason = trv.getFailureReason()
            if failureReason:
                if failureReason.hasTraceback():
                    fObj = open(os.path.join(troveDir, 'traceback.log'), 'w')
                    fObj.write(failureReason.getTraceback())
                    if toStdout:
                        sys.stdout.write(failureReason.getTraceback())
                    fObj.close()

            if toStdout:
                print >> sys.stdout
                sys.stdout.flush()
<commit_msg>Make use of rmake's isolateTroves feature to speed up Conary builds<commit_after>#
# Copyright (c) rPath, Inc.
#

'''
Build, commit, and process troves in "batches"
'''

import logging
import os
import sys
import time

from conary.build.macros import Macros
from rmake.cmdline import monitor

from bob import commit
from bob import flavors
from bob import test
from bob.errors import JobFailedError, TestFailureError
from bob.util import ContextCache, StatusOnlyDisplay
from bob.util import partial, pushStopHandler, popStopHandler

log = logging.getLogger('bob.cook')

def stopJob(batch, signum, _):
    '''
    Signal handler used during a cook job that will stop the job
    and exit.
    '''

    log.error('Caught signal %d during build; stopping job', signum)
    batch.stop()
    sys.exit('Signalled stop')

class Batch(object):
    '''
    A batch of troves to be built at once; e.g. packages or groups. A
    batch may contain either troves or groups but not both, and if it
    includes groups, none of the groups may add another group also in
    the same batch.
    '''

    def __init__(self, clientHelper):
        self._helper = clientHelper

        # setup
        self._contextCache = ContextCache(self._helper.cfg)
        self._bobTroves = []
        self._troves = set()
        self._commit = None

        # job state
        self._jobId = None

        # results
        self._testSuite = None
        self._coverageData = None

    def isEmpty(self):
        """
        Returns C{True} if there are no troves in this job.
        """
        return not self._troves

    def addTrove(self, bobTrove):
        '''
        Add a I{BobPackage} to the batch.

        @type bobTrove: L{bob.trove.BobPackage}
        '''

        _macros = Macros(self._helper.plan.macros)
        macros = {}
        config = bobTrove.getTargetConfig()

        doCommit = not config.noCommit
        if self._commit is None:
            self._commit = doCommit
        elif self._commit != doCommit:
            senseA = doCommit and "wants" or "does not want"
            senseB = doCommit and "do not" or "do"
            log.error("Package %s %s to commit, but existing packages "
                "in batch %s", bobTrove.getPackageName(), senseA, senseB)
            log.error("Either all packages in a batch must commit, or none can")
            raise RuntimeError("Can't commit part of a batch")

        if config:
            for key, value in config.macros.iteritems():
                if key not in self._helper.plan.skipMacros:
                    macros[key] = value % _macros

        # Reduce the set of flavors to build
        oldFlavors = bobTrove.getFlavors()
        newFlavors = flavors.reduce_flavors(bobTrove.getPackageName(),
            config, oldFlavors)

        if len(newFlavors) != len(oldFlavors):
            log.debug('Package %s would be built in %d flavors; '
                'now built in %d flavors', bobTrove.getPackageName(),
                len(oldFlavors), len(newFlavors))
        else:
            log.debug('Package %s will be built in %d flavors',
                bobTrove.getPackageName(), len(newFlavors))

        for buildFlavor in newFlavors:
            # Calculate build parameters
            searchFlavors = flavors.guess_search_flavors(buildFlavor)

            # Get a context in which to build the trove
            context = self._contextCache.get(buildFlavor, searchFlavors,
                macros)

            # Add the tuple to the build list
            self._bobTroves.append(bobTrove)
            self._troves.add((bobTrove.getName(),
                bobTrove.getDownstreamVersion(), buildFlavor, context))

    def run(self, main):
        '''
        Create and run a rMake build job from the set of added troves,
        process the resulting artifacts, and commit if no errors or
        failed tests were encountered.
        '''

        troveNames = sorted(set(x[0].split(':')[0] for x in self._troves))
        log.info('Creating build job: %s', ' '.join(troveNames))

        # Create rMake job
        cfg = self._helper.cfg
        # If there's only one unique package being built, tell rmake not to do
        # dep ordering. This speeds up builds of core packages like Conary,
        # because otherwise rmake would wait for one flavor to build before
        # starting the other flavor due to dep confusion.
        cfg.isolateTroves = len(set(x[0] for x in self._troves))
        job = self._helper.getrMakeHelper().createBuildJob(list(self._troves),
                buildConfig=cfg)
        jobId = self._helper.getrMakeClient().buildJob(job)
        log.info('Job %d started with these sources:', jobId)
        for trove in self._bobTroves:
            version = trove.getDownstreamVersion()
            log.info(' %s=%s/%s', trove.getName(),
                    version.trailingLabel(), version.trailingRevision())

        # Set a signal handler so we can stop the job if we get
        # interrupted
        self._jobId = jobId
        pushStopHandler(partial(stopJob, self))

        # Watch build (to stdout)
        self._helper.callClientHook('client_preCommand', main,
            None, (self._helper.cfg, self._helper.cfg),
            None, None)
        self._helper.callClientHook('client_preCommand2', main,
            self._helper.getrMakeHelper(), None)
        monitor.monitorJob(self._helper.getrMakeClient(), jobId,
            exitOnFinish=True, displayClass=StatusOnlyDisplay,
            showBuildLogs=self._helper.plan.showBuildLogs)

        # Remove the signal handler now that the job is done
        self._jobId = None
        popStopHandler()

        # Pull out logs
        job = self._helper.getrMakeClient().getJob(jobId)
        self.writeLogs(job)

        # Check for error condition
        if job.isFailed():
            log.error('Job %d failed', jobId)
            raise JobFailedError(jobId=jobId, why='Job failed')
        elif not job.isFinished():
            log.error('Job %d is not done, yet watch returned early!', jobId)
            raise JobFailedError(jobId=jobId, why='Job not done')
        elif not list(job.iterBuiltTroves()):
            log.error('Job %d has no built troves', jobId)
            raise JobFailedError(jobId=jobId, why='Job built no troves')

        # Fetch test/coverage output and report results
        self._testSuite, self._coverageData = test.processTests(self._helper,
            job)
        print 'Batch results:', self._testSuite.describe()

        # Bail out without committing if tests failed ...
        if not self._testSuite.isSuccessful():
            log.error('Some tests failed, aborting')
            raise TestFailureError()

        # ... or if all packages are set not to commit
        if self._commit is False:
            return {}

        # Commit to target repository
        if job.isCommitting():
            log.error('Job %d is already committing ' \
                '(probably to the wrong place)', jobId)
            raise JobFailedError(jobId=jobId, why='Job already committing')

        startTime = time.time()
        log.info('Starting commit of job %d', jobId)
        self._helper.getrMakeClient().startCommit([jobId])

        try:
            mapping = commit.commit(self._helper, job)
        except Exception, e_value:
            self._helper.getrMakeClient().commitFailed([jobId], str(e_value))
            raise
        else:
            self._helper.getrMakeClient().commitSucceeded(mapping)
            log.info('Commit of job %d completed in %.02f seconds',
                jobId, time.time() - startTime)
        return mapping

    def stop(self):
        '''
        Stop the currently running build.
        '''
        if self._jobId:
            self._helper.getrMakeHelper().stopJob(self._jobId)

    def getTestSuite(self):
        '''
        Retrieve testsuite data compiled after a batch is run.

        @rtype: L{TestSuite<bob.test.TestSuite>}
        '''
        return self._testSuite

    def getCoverageData(self):
        '''
        Retrieve coverage data compiled after a batch is run.

        @rtype: C{dict([(filename,
                (set([statements]), set([missing])))])}
        '''
        return self._coverageData

    def writeLogs(self, job):
        """
        Write build logs for job C{job} to the output directory.
        """
        jobDir = os.path.join('output', 'logs', str(job.jobId))
        client = self._helper.getrMakeClient()
        for trv in job.iterTroves():
            troveName = '%s{%s}' % (trv.getName(), trv.getContext())
            troveDir = os.path.join(jobDir, troveName)
            if not os.path.isdir(troveDir):
                os.makedirs(troveDir)

            toStdout = False
            prefix = '[%s] ' % troveName
            if trv.isFailed():
                toStdout = True
                print >> sys.stderr, 'Trove %s failed to build:' % troveName
                sys.stderr.flush()

            troveLog = open(os.path.join(troveDir, 'trove.log'), 'w')
            mark = 0
            while True:
                logs = client.getTroveLogs(job.jobId,
                    trv.getNameVersionFlavor(True), mark)
                if not logs:
                    break
                mark += len(logs)

                for timeStamp, message, _ in logs:
                    for line in message.splitlines():
                        line = '[%s] %s\n' % (timeStamp, line.rstrip())
                        troveLog.write(line)
                        if toStdout:
                            sys.stdout.write(prefix + line)
            troveLog.close()

            buildLog = open(os.path.join(troveDir, 'build.log'), 'w')
            mark = 0
            while True:
                _, logs, mark = client.getTroveBuildLog(job.jobId,
                    trv.getNameVersionFlavor(True), mark)
                if not logs:
                    break
                mark += len(logs)
                buildLog.write(logs)
                if toStdout:
                    for line in logs.splitlines():
                        print prefix + line.rstrip()
            buildLog.close()

            failureReason = trv.getFailureReason()
            if failureReason:
                if failureReason.hasTraceback():
                    fObj = open(os.path.join(troveDir, 'traceback.log'), 'w')
                    fObj.write(failureReason.getTraceback())
                    if toStdout:
                        sys.stdout.write(failureReason.getTraceback())
                    fObj.close()

            if toStdout:
                print >> sys.stdout
                sys.stdout.flush()
<|endoftext|>


<commit_before>import logging

from django.conf import settings
from django.utils.importlib import import_module
from django.db.models import get_apps
from django.contrib.auth.management import create_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand

from mapentity import registry
from mapentity.registry import create_mapentity_model_permissions

from geotrek.common.mixins import BasePublishableMixin

logger = logging.getLogger(__name__)

class Command(BaseCommand):
    help = "Create models permissions"

    def execute(self, *args, **options):
        logger.info("Synchronize django permissions")

        for app in get_apps():
            create_permissions(app, [], int(options.get('verbosity', 1)))

        logger.info("Done.")

        logger.info("Synchronize mapentity permissions")

        # Make sure apps are registered at this point
        import_module(settings.ROOT_URLCONF)

        # For all models registered, add missing bits
        for model in registry.registry.keys():
            create_mapentity_model_permissions(model)

        logger.info("Done.")

        logger.info("Synchronize geotrek permissions")

        for content_type in ContentType.objects.all():
            if issubclass(content_type.model_class(), BasePublishableMixin):
                Permission.objects.get_or_create(
                    codename='publish_%s' % content_type.model,
                    name='Can publish %s' % content_type.name,
                    content_type=content_type)

        logger.info("Done.")
<commit_msg>Fix update_permission command for legacy content types<commit_after>import logging

from django.conf import settings
from django.utils.importlib import import_module
from django.db.models import get_apps
from django.contrib.auth.management import create_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand

from mapentity import registry
from mapentity.registry import create_mapentity_model_permissions

from geotrek.common.mixins import BasePublishableMixin

logger = logging.getLogger(__name__)

class Command(BaseCommand):
    help = "Create models permissions"

    def execute(self, *args, **options):
        logger.info("Synchronize django permissions")

        for app in get_apps():
            create_permissions(app, [], int(options.get('verbosity', 1)))

        logger.info("Done.")

        logger.info("Synchronize mapentity permissions")

        # Make sure apps are registered at this point
        import_module(settings.ROOT_URLCONF)

        # For all models registered, add missing bits
        for model in registry.registry.keys():
            create_mapentity_model_permissions(model)

        logger.info("Done.")

        logger.info("Synchronize geotrek permissions")

        for content_type in ContentType.objects.all():
            model = content_type.model_class()
            if model and issubclass(model, BasePublishableMixin):
                Permission.objects.get_or_create(
                    codename='publish_%s' % content_type.model,
                    name='Can publish %s' % content_type.name,
                    content_type=content_type)

        logger.info("Done.")
<|endoftext|>