<commit_before># Copyright ClusterHQ Inc.  See LICENSE file for details.

"""
Client for the Flocker REST API.
"""

from uuid import UUID, uuid4
from json import dumps

from zope.interface import Interface, implementer

from pyrsistent import PClass, field, pmap_field, pmap

from twisted.internet.defer import succeed, fail
from twisted.python.filepath import FilePath
from twisted.web.http import CREATED, OK, CONFLICT

from treq import json_content, content

from ..ca import treq_with_authentication

class Dataset(PClass):
    """
    A dataset in the configuration.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int maximum_size: Size of new dataset, in bytes.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr metadata: A mapping between unicode keys and values.
    :attr bool deleted: If true indicates this dataset should be deleted.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=int, mandatory=True)
    deleted = field(type=bool, mandatory=True, initial=False)
    metadata = pmap_field(unicode, unicode)

class DatasetState(PClass):
    """
    The state of a dataset in the cluster.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int maximum_size: Size of new dataset, in bytes.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr FilePath|None path: Filesytem path where the dataset is mounted,
        or ``None`` if not mounted.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=int, mandatory=True)
    path = field(mandatory=True)

class DatasetAlreadyExists(Exception):
    """
    The suggested dataset ID already exists.
    """

class IFlockerAPIV1Client(Interface):
    """
    The Flocker REST API v1 client.
    """
    def create_dataset(primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        """
        Create a new dataset in the configuration.

        :param UUID primary: The node where the dataset should manifest.
        :param int maximum_size: Size of new dataset, in bytes.
        :param dataset_id: If given, the UUID to use for the dataset.
        :param metadata: A mapping between unicode keys and values, to be
            stored as dataset metadata.

        :return: ``Deferred`` that fires after the configuration has been
            updated with resulting ``Dataset``, or errbacking with
            ``DatasetAlreadyExists``.
        """

    def move_dataset(primary, dataset_id):
        """
        Move the dataset to a new location.

        :param UUID primary: The node where the dataset should manifest.
        :param dataset_id: Which dataset to move.

        :return: ``Deferred`` that fires after the configuration has been
            updated with the resulting ``Dataset``.
        """

    def list_datasets_configuration():
        """
        Return the configured datasets.

        :return: ``Deferred`` firing with iterable of ``Dataset``.
        """

    def list_datasets_state():
        """
        Return the actual datasets in the cluster.

        :return: ``Deferred`` firing with iterable of ``DatasetState``.
        """

@implementer(IFlockerAPIV1Client)
class FakeFlockerClient(object):
    """
    Fake in-memory implementation of ``IFlockerAPIV1Client``.
    """
    def __init__(self):
        self._configured_datasets = pmap()
        self.synchronize_state()

    def create_dataset(self, primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        # In real implementation the server will generate the new ID, but
        # we have to do it ourselves:
        if dataset_id is None:
            dataset_id = uuid4()
        if dataset_id in self._configured_datasets:
            return fail(DatasetAlreadyExists())
        result = Dataset(primary=primary, maximum_size=maximum_size,
                         dataset_id=dataset_id, metadata=metadata)
        self._configured_datasets = self._configured_datasets.set(
            dataset_id, result)
        return succeed(result)

    def move_dataset(self, primary, dataset_id):
        self._configured_datasets = self._configured_datasets.transform(
            [dataset_id, "primary"], primary)
        return succeed(self._configured_datasets[dataset_id])

    def list_datasets_configuration(self):
        return succeed(self._configured_datasets.values())

    def list_datasets_state(self):
        return succeed(self._state_datasets)

    def synchronize_state(self):
        """
        Copy configuration into state.
        """
        self._state_datasets = [
            DatasetState(
                dataset_id=dataset.dataset_id,
                primary=dataset.primary,
                maximum_size=dataset.maximum_size,
                path=FilePath(b"/flocker/{}".format(dataset.dataset_id)))
            for dataset in self._configured_datasets.values()]

class ResponseError(Exception):
    """
    An unexpected response from the REST API.
    """
    def __init__(self, code, body):
        Exception.__init__(self, "Unexpected response code {}:\n{}\n".format(
            code, body))
        self.code = code

@implementer(IFlockerAPIV1Client)
class FlockerClient(object):
    """
    A client for the Flocker V1 REST API.
    """
    def __init__(self, reactor, host, port,
                 ca_cluster_path, cert_path, key_path):
        """
        :param reactor: Reactor to use for connections.
        :param bytes host: Host to connect to.
        :param int port: Port to connect to:
        :param FilePath ca_cluster_path: Path to cluster's CA certificate.
        :param FilePath cert_path: Path to user certificate.
        :param FilePath key_path: Path to user private key.
        """
        self._treq = treq_with_authentication(reactor, ca_cluster_path,
                                              cert_path, key_path)
        self._base_url = b"https://{}:{}/v1".format(host, port)

    def _request(self, method, path, body, success_code, error_codes={}):
        """
        Send a HTTP request to the Flocker API, return decoded JSON body.

        :param bytes method: HTTP method, e.g. PUT.
        :param bytes path: Path to add to base URL.
        :param body: If not ``None``, JSON encode this and send as the
            body of the request.
        :param int success_code: Expected success response code.
        :param error_codes: Mapping from HTTP response code to exception to be
            raised if it is present.

        :return: ``Deferred`` firing with decoded JSON.
        """
        def error(body, code):
            if code in error_codes:
                raise error_codes[code](body)
            raise ResponseError(code, body)

        def got_result(result):
            if result.code == success_code:
                return json_content(result)
            else:
                d = content(result)
                d.addCallback(error, result.code)
                return d

        headers = None
        data = None
        if body is not None:
            headers = {b"content-type": b"application/json"}
            data = dumps(body)
        request = self._treq.request(
            method, self._base_url + path,
            data=data, headers=headers,
            # Keep tests from having dirty reactor problems:
            persistent=False
        )
        request.addCallback(got_result)
        return request

    def _parse_configuration_dataset(self, dataset_dict):
        """
        Convert a dictionary decoded from JSON with a dataset's configuration.

        :param dataset_dict: Dictionary describing a dataset.
        :return: ``Dataset`` instance.
        """
        return Dataset(primary=UUID(dataset_dict[u"primary"]),
                       maximum_size=dataset_dict[u"maximum_size"],
                       dataset_id=UUID(dataset_dict[u"dataset_id"]),
                       metadata=dataset_dict[u"metadata"])

    def create_dataset(self, primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        dataset = {u"primary": unicode(primary),
                   u"maximum_size": maximum_size,
                   u"metadata": dict(metadata)}
        if dataset_id is not None:
            dataset[u"dataset_id"] = unicode(dataset_id)

        request = self._request(b"POST", b"/configuration/datasets",
                                dataset, CREATED,
                                {CONFLICT: DatasetAlreadyExists})
        request.addCallback(self._parse_configuration_dataset)
        return request

    def move_dataset(self, primary, dataset_id):
        request = self._request(
            b"POST", b"/configuration/datasets/{}".format(dataset_id),
            {u"primary": unicode(primary)}, OK)
        request.addCallback(self._parse_configuration_dataset)
        return request

    def list_datasets_configuration(self):
        request = self._request(b"GET", b"/configuration/datasets", None, OK)
        request.addCallback(
            lambda results:
            [self._parse_configuration_dataset(d) for d in results])
        return request

    def list_datasets_state(self):
        request = self._request(b"GET", b"/state/datasets", None, OK)

        def parse_dataset_state(dataset_dict):
            return DatasetState(primary=UUID(dataset_dict[u"primary"]),
                                maximum_size=dataset_dict[u"maximum_size"],
                                dataset_id=UUID(dataset_dict[u"dataset_id"]),
                                path=FilePath(dataset_dict[u"path"]))

        request.addCallback(
            lambda results: [parse_dataset_state(d) for d in results])
        return request
<commit_msg>Address review comment: No default mutables.<commit_after># Copyright ClusterHQ Inc.  See LICENSE file for details.

"""
Client for the Flocker REST API.
"""

from uuid import UUID, uuid4
from json import dumps

from zope.interface import Interface, implementer

from pyrsistent import PClass, field, pmap_field, pmap

from twisted.internet.defer import succeed, fail
from twisted.python.filepath import FilePath
from twisted.web.http import CREATED, OK, CONFLICT

from treq import json_content, content

from ..ca import treq_with_authentication

class Dataset(PClass):
    """
    A dataset in the configuration.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int maximum_size: Size of new dataset, in bytes.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr metadata: A mapping between unicode keys and values.
    :attr bool deleted: If true indicates this dataset should be deleted.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=int, mandatory=True)
    deleted = field(type=bool, mandatory=True, initial=False)
    metadata = pmap_field(unicode, unicode)

class DatasetState(PClass):
    """
    The state of a dataset in the cluster.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int maximum_size: Size of new dataset, in bytes.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr FilePath|None path: Filesytem path where the dataset is mounted,
        or ``None`` if not mounted.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=int, mandatory=True)
    path = field(mandatory=True)

class DatasetAlreadyExists(Exception):
    """
    The suggested dataset ID already exists.
    """

class IFlockerAPIV1Client(Interface):
    """
    The Flocker REST API v1 client.
    """
    def create_dataset(primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        """
        Create a new dataset in the configuration.

        :param UUID primary: The node where the dataset should manifest.
        :param int maximum_size: Size of new dataset, in bytes.
        :param dataset_id: If given, the UUID to use for the dataset.
        :param metadata: A mapping between unicode keys and values, to be
            stored as dataset metadata.

        :return: ``Deferred`` that fires after the configuration has been
            updated with resulting ``Dataset``, or errbacking with
            ``DatasetAlreadyExists``.
        """

    def move_dataset(primary, dataset_id):
        """
        Move the dataset to a new location.

        :param UUID primary: The node where the dataset should manifest.
        :param dataset_id: Which dataset to move.

        :return: ``Deferred`` that fires after the configuration has been
            updated with the resulting ``Dataset``.
        """

    def list_datasets_configuration():
        """
        Return the configured datasets.

        :return: ``Deferred`` firing with iterable of ``Dataset``.
        """

    def list_datasets_state():
        """
        Return the actual datasets in the cluster.

        :return: ``Deferred`` firing with iterable of ``DatasetState``.
        """

@implementer(IFlockerAPIV1Client)
class FakeFlockerClient(object):
    """
    Fake in-memory implementation of ``IFlockerAPIV1Client``.
    """
    def __init__(self):
        self._configured_datasets = pmap()
        self.synchronize_state()

    def create_dataset(self, primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        # In real implementation the server will generate the new ID, but
        # we have to do it ourselves:
        if dataset_id is None:
            dataset_id = uuid4()
        if dataset_id in self._configured_datasets:
            return fail(DatasetAlreadyExists())
        result = Dataset(primary=primary, maximum_size=maximum_size,
                         dataset_id=dataset_id, metadata=metadata)
        self._configured_datasets = self._configured_datasets.set(
            dataset_id, result)
        return succeed(result)

    def move_dataset(self, primary, dataset_id):
        self._configured_datasets = self._configured_datasets.transform(
            [dataset_id, "primary"], primary)
        return succeed(self._configured_datasets[dataset_id])

    def list_datasets_configuration(self):
        return succeed(self._configured_datasets.values())

    def list_datasets_state(self):
        return succeed(self._state_datasets)

    def synchronize_state(self):
        """
        Copy configuration into state.
        """
        self._state_datasets = [
            DatasetState(
                dataset_id=dataset.dataset_id,
                primary=dataset.primary,
                maximum_size=dataset.maximum_size,
                path=FilePath(b"/flocker/{}".format(dataset.dataset_id)))
            for dataset in self._configured_datasets.values()]

class ResponseError(Exception):
    """
    An unexpected response from the REST API.
    """
    def __init__(self, code, body):
        Exception.__init__(self, "Unexpected response code {}:\n{}\n".format(
            code, body))
        self.code = code

@implementer(IFlockerAPIV1Client)
class FlockerClient(object):
    """
    A client for the Flocker V1 REST API.
    """
    def __init__(self, reactor, host, port,
                 ca_cluster_path, cert_path, key_path):
        """
        :param reactor: Reactor to use for connections.
        :param bytes host: Host to connect to.
        :param int port: Port to connect to:
        :param FilePath ca_cluster_path: Path to cluster's CA certificate.
        :param FilePath cert_path: Path to user certificate.
        :param FilePath key_path: Path to user private key.
        """
        self._treq = treq_with_authentication(reactor, ca_cluster_path,
                                              cert_path, key_path)
        self._base_url = b"https://{}:{}/v1".format(host, port)

    def _request(self, method, path, body, success_code, error_codes=None):
        """
        Send a HTTP request to the Flocker API, return decoded JSON body.

        :param bytes method: HTTP method, e.g. PUT.
        :param bytes path: Path to add to base URL.
        :param body: If not ``None``, JSON encode this and send as the
            body of the request.
        :param int success_code: Expected success response code.
        :param error_codes: Mapping from HTTP response code to exception to be
            raised if it is present, or ``None`` to send no headers.

        :return: ``Deferred`` firing with decoded JSON.
        """
        if error_codes is None:
            error_codes = {}

        def error(body, code):
            if code in error_codes:
                raise error_codes[code](body)
            raise ResponseError(code, body)

        def got_result(result):
            if result.code == success_code:
                return json_content(result)
            else:
                d = content(result)
                d.addCallback(error, result.code)
                return d

        headers = None
        data = None
        if body is not None:
            headers = {b"content-type": b"application/json"}
            data = dumps(body)
        request = self._treq.request(
            method, self._base_url + path,
            data=data, headers=headers,
            # Keep tests from having dirty reactor problems:
            persistent=False
        )
        request.addCallback(got_result)
        return request

    def _parse_configuration_dataset(self, dataset_dict):
        """
        Convert a dictionary decoded from JSON with a dataset's configuration.

        :param dataset_dict: Dictionary describing a dataset.
        :return: ``Dataset`` instance.
        """
        return Dataset(primary=UUID(dataset_dict[u"primary"]),
                       maximum_size=dataset_dict[u"maximum_size"],
                       dataset_id=UUID(dataset_dict[u"dataset_id"]),
                       metadata=dataset_dict[u"metadata"])

    def create_dataset(self, primary, maximum_size, dataset_id=None,
                       metadata=pmap()):
        dataset = {u"primary": unicode(primary),
                   u"maximum_size": maximum_size,
                   u"metadata": dict(metadata)}
        if dataset_id is not None:
            dataset[u"dataset_id"] = unicode(dataset_id)

        request = self._request(b"POST", b"/configuration/datasets",
                                dataset, CREATED,
                                {CONFLICT: DatasetAlreadyExists})
        request.addCallback(self._parse_configuration_dataset)
        return request

    def move_dataset(self, primary, dataset_id):
        request = self._request(
            b"POST", b"/configuration/datasets/{}".format(dataset_id),
            {u"primary": unicode(primary)}, OK)
        request.addCallback(self._parse_configuration_dataset)
        return request

    def list_datasets_configuration(self):
        request = self._request(b"GET", b"/configuration/datasets", None, OK)
        request.addCallback(
            lambda results:
            [self._parse_configuration_dataset(d) for d in results])
        return request

    def list_datasets_state(self):
        request = self._request(b"GET", b"/state/datasets", None, OK)

        def parse_dataset_state(dataset_dict):
            return DatasetState(primary=UUID(dataset_dict[u"primary"]),
                                maximum_size=dataset_dict[u"maximum_size"],
                                dataset_id=UUID(dataset_dict[u"dataset_id"]),
                                path=FilePath(dataset_dict[u"path"]))

        request.addCallback(
            lambda results: [parse_dataset_state(d) for d in results])
        return request
<|endoftext|>


<commit_before>from libpysal import cg
import numpy as np

def compute_length(v0, v1):
    """Compute the euclidean distance between two points.

    Parameters
    ----------
    v0 : tuple
        sequence in the form x, y
    vq : tuple
        sequence in the form x, y
    Returns
    --------
    euc_dist : float
        Euclidean distance
    """
    euc_dist = np.sqrt((v0[0] - v1[0])**2 + (v0[1] - v1[1])**2)
    return euc_dist

def get_neighbor_distances(ntw, v0, l):
    """Get distances to the nearest node neighbors along connecting edges.

    Parameters
    ----------
    ntw : spaghetti.Network
        spaghetti Network object.
    v0 : int
        Node id
    l : dict
        key is tuple (start node, end node); value is float.
        Cost per edge to travel, e.g. distance.

    Returns
    -------
    neighbors : dict
        key is int (node id); value is float (distance)
    """
    edges = ntw.enum_links_node(v0)
    neighbors = {}
    for e in edges:
        if e[0] != v0:
            neighbors[e[0]] = l[e]
        else:
            neighbors[e[1]] = l[e]
    return neighbors

def generatetree(pred):
    """Rebuild the shortest path from root origin to destination

    Parameters
    ----------
    pred : list
        List of preceeding nodes for traversal route.
    Returns
    --------
    tree : dict
        key is root origin; value is root origin to destination.
    """
    tree = {}
    for i, p in enumerate(pred):
        if p == -1:
            # root node
            tree[i] = [i]
            continue
        idx = p
        path = [idx]
        while idx >= 0:
            nextnode = pred[idx]
            idx = nextnode
            if idx >= 0:
                path.append(nextnode)
        tree[i] = path
    return tree

def dijkstra(ntw, cost, v0, n=float('inf')):
    """Compute the shortest path between a start node and all other nodes in
    an origin-destination matrix.

    Parameters
    ----------
    ntw :  spaghetti.Network
        spaghetti Network object.
    cost : dict
        key is tuple (start node, end node); value is float.
        Cost per edge to travel, e.g. distance.
    v0 : int
        Start node ID
    n : float
        integer break point to stop iteration and return n neighbors.
        Default is ('inf').

    Returns
    -------
    distance : list
        List of distances from node to all other nodes.
    pred : list
        List of preceeding nodes for traversal route.
    """
    distance = [n for x in ntw.node_list]
    idx = ntw.node_list.index(v0)
    distance[ntw.node_list.index(v0)] = 0
    pred = [-1 for x in ntw.node_list]
    a = set()
    a.add(v0)
    while len(a) > 0:
        # Get node with the lowest value from distance.
        dist = n
        for node in a:
            if distance[node] < dist:
                dist = distance[node]
                v = node
        # Remove that node from the set.
        a.remove(v)
        last = v
        # 4. Get the neighbors to the current node.
        neighbors = get_neighbor_distances(ntw, v, cost)
        for v1, indiv_cost in neighbors.items():
            if distance[v1] > distance[v] + indiv_cost:
                distance[v1] = distance[v] + indiv_cost
                pred[v1] = v
                a.add(v1)
    pred = np.array(pred, dtype=np.int)
    return distance, pred

def dijkstra_mp(ntw_cost_node):
    """
    Compute the shortest path between a start node and all other
    nodes in the web utilizing multiple cores upon request.

    Parameters
    ----------
    ntw_cost_node : tuple
        tuple of arguments to pass into dijkstra
        (1) ntw - spaghetti.Network; spaghetti Network object.
        (2) cost - dict; key is tuple (start node, end node); value is float
                   Cost per edge to travel, e.g. distance
        (3) node - int; Start node ID

    Returns
    -------
    distance : list
        List of distances from node to all other nodes.
    pred : list
        List of preceeding nodes for traversal route.
    """
    ntw, cost, node = ntw_cost_node
    distance, pred = dijkstra(ntw, cost, node)
    return distance, pred

def squaredDistancePointSegment(point, segment):
    """Find the squared distance between a point and a segment.

    Parameters
    ---------
    point : tuple
        point coordinates (x,y)
    segment : list
        List of 2 point coordinate tuples [(x0,y0), (x1,y1)].

    Returns
    -------
    sqd : float
        distance squared between point and segment
    nearp : numpy.ndarray
        array of (xb, yb); the nearest point on the segment

    Example
    -------
    >>> import spaghetti.util as util
    >>> point, segment = (1,1), ((0,0), (2,0))
    >>> util.squaredDistancePointSegment(point, segment)
     1.0, array([1., 0.]))
    """
    #
    p0, p1 = [np.array(p) for p in segment]
    v = p1 - p0
    p = np.array(point)
    w = p - p0
    c1 = np.dot(w, v)
    if c1 <= 0.:
        sqd = np.dot(w.T, w)
        nearp = p0
        return sqd, nearp
    #
    c2 = np.dot(v, v)
    if c2 <= c1:
        dp1 = p - p1
        sqd = np.dot(dp1.T, dp1)
        nearp = p1
        return sqd, nearp
    #
    b = c1 / c2
    bv = np.dot(b, v)
    pb = p0 + bv
    d2 = p - pb
    sqd = np.dot(d2, d2)
    nearp = pb
    return sqd, nearp

def snapPointsOnSegments(points, segments):
    """Place points onto closet segment in a set of segments

    Arguments
    ---------
    points : dict
        Point id as key and (x,y) coordinate as value
    segments : list
        Elements are of type pysal.cg.shapes.Chain
        ** Note ** each element is a segment represented as a chain with
        *one head and one tail node* in other words one link only.

    Returns
    -------
    p2s : dict
        key [point id (see points in arguments)]; value [a 2-tuple
        ((head, tail), point) where (head, tail) is the target segment,
        and point is the snapped location on the segment.
    """

    # Put segments in an Rtree.
    rt = cg.Rtree()
    SMALL = np.finfo(float).eps
    node2segs = {}

    for segment in segments:
        head, tail = segment.vertices
        x0, y0 = head
        x1, y1 = tail
        if (x0, y0) not in node2segs:
            node2segs[(x0, y0)] = []
        if (x1, y1) not in node2segs:
            node2segs[(x1, y1)] = []
        node2segs[(x0, y0)].append(segment)
        node2segs[(x1, y1)].append(segment)
        x0, y0, x1, y1 = segment.bounding_box
        x0 -= SMALL
        y0 -= SMALL
        x1 += SMALL
        y1 += SMALL
        r = cg.Rect(x0, y0, x1, y1)
        rt.insert(segment, r)

    # Build a KDtree on segment nodes.
    kt = cg.KDTree(list(node2segs.keys()))
    p2s = {}

    for ptIdx, point in points.items():
        # First, find nearest neighbor segment node for the point.
        dmin, node = kt.query(point, k=1)
        node = tuple(kt.data[node])
        closest = node2segs[node][0].vertices

        # Use this segment as the candidate closest segment:  closest
        # Use the distance as the distance to beat:           dmin
        p2s[ptIdx] = (closest, np.array(node))
        x0 = point[0] - dmin
        y0 = point[1] - dmin
        x1 = point[0] + dmin
        y1 = point[1] + dmin

        # Find all segments with bounding boxes that intersect
        # a query rectangle centered on the point with sides of length 2*dmin.
        candidates = [cand for cand in rt.intersection([x0, y0, x1, y1])]
        dmin += SMALL
        dmin2 = dmin * dmin

        # Of the candidate segments, find the nearest to the query point.
        for candidate in candidates:
            dnc, p2b = squaredDistancePointSegment(point, candidate.vertices)
            if dnc <= dmin2:
                closest = candidate.vertices
                dmin2 = dnc
                p2s[ptIdx] = (closest, p2b)

    return p2s<commit_msg>docstrings cleanup [15]<commit_after>from libpysal import cg
import numpy as np

def compute_length(v0, v1):
    """Compute the euclidean distance between two points.

    Parameters
    ----------
    v0 : tuple
        sequence in the form x, y
    vq : tuple
        sequence in the form x, y
    Returns
    --------
    euc_dist : float
        Euclidean distance
    """
    euc_dist = np.sqrt((v0[0] - v1[0])**2 + (v0[1] - v1[1])**2)
    return euc_dist

def get_neighbor_distances(ntw, v0, l):
    """Get distances to the nearest node neighbors along connecting edges.

    Parameters
    ----------
    ntw : spaghetti.Network
        spaghetti Network object.
    v0 : int
        Node id
    l : dict
        key is tuple (start node, end node); value is float.
        Cost per edge to travel, e.g. distance.

    Returns
    -------
    neighbors : dict
        key is int (node id); value is float (distance)
    """
    edges = ntw.enum_links_node(v0)
    neighbors = {}
    for e in edges:
        if e[0] != v0:
            neighbors[e[0]] = l[e]
        else:
            neighbors[e[1]] = l[e]
    return neighbors

def generatetree(pred):
    """Rebuild the shortest path from root origin to destination

    Parameters
    ----------
    pred : list
        List of preceeding nodes for traversal route.
    Returns
    --------
    tree : dict
        key is root origin; value is root origin to destination.
    """
    tree = {}
    for i, p in enumerate(pred):
        if p == -1:
            # root node
            tree[i] = [i]
            continue
        idx = p
        path = [idx]
        while idx >= 0:
            nextnode = pred[idx]
            idx = nextnode
            if idx >= 0:
                path.append(nextnode)
        tree[i] = path
    return tree

def dijkstra(ntw, cost, v0, n=float('inf')):
    """Compute the shortest path between a start node and all other nodes in
    an origin-destination matrix.

    Parameters
    ----------
    ntw :  spaghetti.Network
        spaghetti Network object.
    cost : dict
        key is tuple (start node, end node); value is float.
        Cost per edge to travel, e.g. distance.
    v0 : int
        Start node ID
    n : float
        integer break point to stop iteration and return n neighbors.
        Default is ('inf').

    Returns
    -------
    distance : list
        List of distances from node to all other nodes.
    pred : list
        List of preceeding nodes for traversal route.
    """
    distance = [n for x in ntw.node_list]
    idx = ntw.node_list.index(v0)
    distance[ntw.node_list.index(v0)] = 0
    pred = [-1 for x in ntw.node_list]
    a = set()
    a.add(v0)
    while len(a) > 0:
        # Get node with the lowest value from distance.
        dist = n
        for node in a:
            if distance[node] < dist:
                dist = distance[node]
                v = node
        # Remove that node from the set.
        a.remove(v)
        last = v
        # 4. Get the neighbors to the current node.
        neighbors = get_neighbor_distances(ntw, v, cost)
        for v1, indiv_cost in neighbors.items():
            if distance[v1] > distance[v] + indiv_cost:
                distance[v1] = distance[v] + indiv_cost
                pred[v1] = v
                a.add(v1)
    pred = np.array(pred, dtype=np.int)
    return distance, pred

def dijkstra_mp(ntw_cost_node):
    """
    Compute the shortest path between a start node and all other
    nodes in the web utilizing multiple cores upon request.

    Parameters
    ----------
    ntw_cost_node : tuple
        tuple of arguments to pass into dijkstra
        (1) ntw - spaghetti.Network; spaghetti Network object.
        (2) cost - dict; key is tuple (start node, end node); value is float
                   Cost per edge to travel, e.g. distance
        (3) node - int; Start node ID

    Returns
    -------
    distance : list
        List of distances from node to all other nodes.
    pred : list
        List of preceeding nodes for traversal route.
    """
    ntw, cost, node = ntw_cost_node
    distance, pred = dijkstra(ntw, cost, node)
    return distance, pred

def squaredDistancePointSegment(point, segment):
    """Find the squared distance between a point and a segment.

    Parameters
    ---------
    point : tuple
        point coordinates (x,y)
    segment : list
        List of 2 point coordinate tuples [(x0,y0), (x1,y1)].

    Returns
    -------
    sqd : float
        distance squared between point and segment
    nearp : numpy.ndarray
        array of (xb, yb); the nearest point on the segment

    Example
    -------
    >>> import spaghetti.util as util
    >>> point, segment = (1,1), ((0,0), (2,0))
    >>> util.squaredDistancePointSegment(point, segment)
     1.0, array([1., 0.]))
    """
    #
    p0, p1 = [np.array(p) for p in segment]
    v = p1 - p0
    p = np.array(point)
    w = p - p0
    c1 = np.dot(w, v)
    if c1 <= 0.:
        sqd = np.dot(w.T, w)
        nearp = p0
        return sqd, nearp
    #
    c2 = np.dot(v, v)
    if c2 <= c1:
        dp1 = p - p1
        sqd = np.dot(dp1.T, dp1)
        nearp = p1
        return sqd, nearp
    #
    b = c1 / c2
    bv = np.dot(b, v)
    pb = p0 + bv
    d2 = p - pb
    sqd = np.dot(d2, d2)
    nearp = pb
    return sqd, nearp

def snapPointsOnSegments(points, segments):
    """Place points onto closet segment in a set of segments

    Arguments
    ---------
    points : dict
        Point id as key and (x,y) coordinate as value
    segments : list
        Elements are of type libpysal.cg.shapes.Chain
        ** Note ** each element is a segment represented as a chain with
        *one head and one tail node* in other words one link only.

    Returns
    -------
    p2s : dict
        key [point id (see points in arguments)]; value [a 2-tuple
        ((head, tail), point) where (head, tail) is the target segment,
        and point is the snapped location on the segment.

    Example
    -------
    >>> import spaghetti.util as util
    >>> from libpysal.cg.shapes import Point, Chain
    >>> points = {0: Point((1,1))}
    >>> segments = [Chain([Point((0,0)), Point((2,0))])]
    >>> util.snapPointsOnSegments(points, segments)
    {0: ([(0.0, 0.0), (2.0, 0.0)], array([1., 0.]))}
    """

    # Put segments in an Rtree.
    rt = cg.Rtree()
    SMALL = np.finfo(float).eps
    node2segs = {}

    for segment in segments:
        head, tail = segment.vertices
        x0, y0 = head
        x1, y1 = tail
        if (x0, y0) not in node2segs:
            node2segs[(x0, y0)] = []
        if (x1, y1) not in node2segs:
            node2segs[(x1, y1)] = []
        node2segs[(x0, y0)].append(segment)
        node2segs[(x1, y1)].append(segment)
        x0, y0, x1, y1 = segment.bounding_box
        x0 -= SMALL
        y0 -= SMALL
        x1 += SMALL
        y1 += SMALL
        r = cg.Rect(x0, y0, x1, y1)
        rt.insert(segment, r)

    # Build a KDtree on segment nodes.
    kt = cg.KDTree(list(node2segs.keys()))
    p2s = {}

    for ptIdx, point in points.items():
        # First, find nearest neighbor segment node for the point.
        dmin, node = kt.query(point, k=1)
        node = tuple(kt.data[node])
        closest = node2segs[node][0].vertices

        # Use this segment as the candidate closest segment:  closest
        # Use the distance as the distance to beat:           dmin
        p2s[ptIdx] = (closest, np.array(node))
        x0 = point[0] - dmin
        y0 = point[1] - dmin
        x1 = point[0] + dmin
        y1 = point[1] + dmin

        # Find all segments with bounding boxes that intersect
        # a query rectangle centered on the point with sides of length 2*dmin.
        candidates = [cand for cand in rt.intersection([x0, y0, x1, y1])]
        dmin += SMALL
        dmin2 = dmin * dmin

        # Of the candidate segments, find the nearest to the query point.
        for candidate in candidates:
            dnc, p2b = squaredDistancePointSegment(point, candidate.vertices)
            if dnc <= dmin2:
                closest = candidate.vertices
                dmin2 = dnc
                p2s[ptIdx] = (closest, p2b)

    return p2s<|endoftext|>


<commit_before># socketwrap
# Author: Blake Oliver <oliver22213@me.com>

from collections import deque
import click
import socket
import select
import subprocess
import time
import threading

# define the command and it's options and args

@click.command()
@click.option('--host', '--hostname', '-hn', default='127.0.0.1', show_default=True, help="""Interface the server should listen on.""")
@click.option('--port', '-p', default=3000, show_default=True, help="""Port the server should bind to.""")
@click.option('--enable-multiple-connections/--disable-multiple-connections', '-e/-E', help="""Allow multiple connections. Each one will be able to send to the subprocess as well as receive.""")
@click.argument('command', nargs=-1, required=True)
def socket_wrap(hostname, port, enable_multiple_connections, command):
	"""Capture a given command's standard input, standard output, and standard error (stdin, stdout, and stderr) streams and let clients send and receive data to it by connecting to this program.
Args:
command: The command this program should wrap (including any arguments).
	Any data received from it's stdout and stderr streams is buffered until the first client connects.
	If the command exits with a non-zero returncode before the server is initialized, it's stderr is printed to the console.
"""
	subproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
	time.sleep(0.2) # wait a bit before checking it's returncode
	r = subproc.poll()
	if r != None:
		if r == 0:
			print("The specified command has exited; not starting server.")
			return
		else: # non-zero returncode
			print("Subcommand exited with a non-zero returncode. It's standard error is:")
			print(subproc.stderr.read())
			return
	# the returncode was none, so the process is running
	# set up the server
	try:
		command_output_queue = deque() # queue of strings sent by the command we're wrapping
		stop_flag = threading.Event()
		t = threading.Thread(target=poll_command_for_output, args=([subproc.stdout, subproc.stderr], command_output_queue, 0.1, stop_flag))
		t.start()
		server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		server_sock.bind((hostname, port))
		server_sock.listen(2)
		print("Command started and server listening on {}:{}".format(hostname, port))
		read = [] # sockets that might have something we can read
		write = [] # sockets that might have free space in their buffer
		error = [] # sockets we need to handle errors for
		all_clients = []
		read.append(server_sock) # add the server socket so we can accept new connections
		while subproc.poll()==None:
			r, w, e = select.select(read, write, error, 3)
			for sock in r: # for each socket that has something to be read
				if sock == server_sock: # this is the server socket, accept the new connection
					con, addr = sock.accept()
					print("""{} has connected.""".format(addr))
					read.append(con)
					write.append(con)
					error.append(con)
					all_clients.append(con)
				else: # socket with something to read is not the server
					# large amounts of data might cause a lockup; it's unlikely though
					f = sock.makefile('r') # use makefile because data should be split by lines
					data = f.readline()
					f.close()
					if data:
						try:
							subproc.stdin.write(data)
							subproc.stdin.flush()
						except IOError as e: # the subprocess has closed
							raise KeyboardInterrupt # trigger an exit
					else: # a client sending an empty string indicates a disconnect
						read.remove(sock)
						print("{} has disconnected.".format(sock.getpeername()))
						if sock in write:
							write.remove(sock)
						if sock in error:
							error.remove(sock)
						all_clients.remove(sock)
						sock.close()
			# now check if the command has any output that needs to be sent to clients
			if command_output_queue and w: # if there is at least one item in the queue and there is at least one socket to send it to
				i = command_output_queue.popleft()
				for sock in w: # for every socket who's buffer is free for writing
					sock.sendall(i)
			# handle sockets with errors
			for sock in e:
				print("Socket {} has an error!".format(sock.getpeername()))
				if sock in rread:
					r.remove(sock)
				if sock in write:
					write.remove(sock)
				error.remove(sock)
				all_clients.remove(sock)
				sock.close()
				print("{} has been disconnected.".format(sock.getpeername()))

		# main loop has exited
		for sock in all_clients:
			sock.send("Process has exited.")
		raise KeyboardInterrupt
	except Exception as e:
		if e is KeyboardInterrupt:
			print("Shutting down.")
		else:
			print("""Shutting down do to error: {}""".format(e))
		stop_flag.set()
		for sock in all_clients:
			sock.send("Shutting down. Goodbye.")
			sock.close()
		server_sock.close()
		subproc.kill()

def poll_command_for_output(handles, output_queue, poll_time, stop_flag):
	"""For every file-like object in the 'handles' list, check every poll_time to see if it has new output. If it does, add it as a string to output_queue."""
	while not stop_flag.is_set():
		for h in handles:
			try:
				buff = h.read(8192)
			except IOError as e:
				pass
			if buff:
				output_queue.append(buff)
		time.sleep(poll_time)

if __name__ == '__main__':
	socket_wrap()<commit_msg>Fix a bug where a socket with an error wouldn't be removed from the list of sockets to read from<commit_after># socketwrap
# Author: Blake Oliver <oliver22213@me.com>

from collections import deque
import click
import socket
import select
import subprocess
import time
import threading

# define the command and it's options and args

@click.command()
@click.option('--host', '--hostname', '-hn', default='127.0.0.1', show_default=True, help="""Interface the server should listen on.""")
@click.option('--port', '-p', default=3000, show_default=True, help="""Port the server should bind to.""")
@click.option('--enable-multiple-connections/--disable-multiple-connections', '-e/-E', help="""Allow multiple connections. Each one will be able to send to the subprocess as well as receive.""")
@click.argument('command', nargs=-1, required=True)
def socket_wrap(hostname, port, enable_multiple_connections, command):
	"""Capture a given command's standard input, standard output, and standard error (stdin, stdout, and stderr) streams and let clients send and receive data to it by connecting to this program.
Args:
command: The command this program should wrap (including any arguments).
	Any data received from it's stdout and stderr streams is buffered until the first client connects.
	If the command exits with a non-zero returncode before the server is initialized, it's stderr is printed to the console.
"""
	subproc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
	time.sleep(0.2) # wait a bit before checking it's returncode
	r = subproc.poll()
	if r != None:
		if r == 0:
			print("The specified command has exited; not starting server.")
			return
		else: # non-zero returncode
			print("Subcommand exited with a non-zero returncode. It's standard error is:")
			print(subproc.stderr.read())
			return
	# the returncode was none, so the process is running
	# set up the server
	try:
		command_output_queue = deque() # queue of strings sent by the command we're wrapping
		stop_flag = threading.Event()
		t = threading.Thread(target=poll_command_for_output, args=([subproc.stdout, subproc.stderr], command_output_queue, 0.1, stop_flag))
		t.start()
		server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		server_sock.bind((hostname, port))
		server_sock.listen(2)
		print("Command started and server listening on {}:{}".format(hostname, port))
		read = [] # sockets that might have something we can read
		write = [] # sockets that might have free space in their buffer
		error = [] # sockets we need to handle errors for
		all_clients = []
		read.append(server_sock) # add the server socket so we can accept new connections
		while subproc.poll()==None:
			r, w, e = select.select(read, write, error, 3)
			for sock in r: # for each socket that has something to be read
				if sock == server_sock: # this is the server socket, accept the new connection
					con, addr = sock.accept()
					print("""{} has connected.""".format(addr))
					read.append(con)
					write.append(con)
					error.append(con)
					all_clients.append(con)
				else: # socket with something to read is not the server
					# large amounts of data might cause a lockup; it's unlikely though
					f = sock.makefile('r') # use makefile because data should be split by lines
					data = f.readline()
					f.close()
					if data:
						try:
							subproc.stdin.write(data)
							subproc.stdin.flush()
						except IOError as e: # the subprocess has closed
							raise KeyboardInterrupt # trigger an exit
					else: # a client sending an empty string indicates a disconnect
						read.remove(sock)
						print("{} has disconnected.".format(sock.getpeername()))
						if sock in write:
							write.remove(sock)
						if sock in error:
							error.remove(sock)
						all_clients.remove(sock)
						sock.close()
			# now check if the command has any output that needs to be sent to clients
			if command_output_queue and w: # if there is at least one item in the queue and there is at least one socket to send it to
				i = command_output_queue.popleft()
				for sock in w: # for every socket who's buffer is free for writing
					sock.sendall(i)
			# handle sockets with errors
			for sock in e:
				print("Socket {} has an error!".format(sock.getpeername()))
				if sock in rread:
					read.remove(sock)
				if sock in write:
					write.remove(sock)
				error.remove(sock)
				all_clients.remove(sock)
				sock.close()
				print("{} has been disconnected.".format(sock.getpeername()))

		# main loop has exited
		for sock in all_clients:
			sock.send("Process has exited.")
		raise KeyboardInterrupt
	except Exception as e:
		if e is KeyboardInterrupt:
			print("Shutting down.")
		else:
			print("""Shutting down do to error: {}""".format(e))
		stop_flag.set()
		for sock in all_clients:
			sock.send("Shutting down. Goodbye.")
			sock.close()
		server_sock.close()
		subproc.kill()

def poll_command_for_output(handles, output_queue, poll_time, stop_flag):
	"""For every file-like object in the 'handles' list, check every poll_time to see if it has new output. If it does, add it as a string to output_queue."""
	while not stop_flag.is_set():
		for h in handles:
			try:
				buff = h.read(8192)
			except IOError as e:
				pass
			if buff:
				output_queue.append(buff)
		time.sleep(poll_time)

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


<commit_before>import Tkinter
import cups
import re
import httplib
import sys
from time import time
import xml.etree.ElementTree as ET

usernameKey = 'job-originating-user-name'
clientKey =   'job-originating-host-name'
titleKey = 'job-name'
uuidKey  = 'job-uuid'
supportedUriKey = 'printer-uri-supported'

class PageServerAuth(object):
   def __init__(self, private, hostname, idGenerator, display, conn):
       self.hostname   = hostname
       self.servername = 'wwwapp.cc.columbia.edu'
       self.idGenerator = idGenerator
       self.messageDisplay = display
       self.conn = conn
       printers = self.conn.getPrinters()
       self.privateUri = printers[private][supportedUriKey]
       self.currentJob = None				       # Support for event Handlers
       self.errorcb = None                                     # Support for event Handlers

   def authorizeJobs(self, selectedJobs, errorcb, authname):
       requestId=self.idGenerator()
       self.errorcb = errorcb

       for j in selectedJobs:
          self.messageDisplay.claimInterlock()
          url = '/atg/PageServer/query/%s/%s/%.5x/%d' % (self.hostname, authname, requestId, j.pages)
          http = httplib.HTTPSConnection(self.servername)
          http.request("GET", url)
          resp = http.getresponse()
          xml = resp.read()
          root = ET.fromstring(xml)
          queryResponse = root.find('queryResponse').attrib
          if queryResponse['Authorized'] == 'True':
               # reset the username on any authorized unclaimed job
               if j.username != authname:
                  j.username = authname
               self.releaseJob(j, queryResponse['reqId'])
          else:
               errorcb('Insufficient Quota')
               self.messageDisplay.quota(root, 'queryResponse', True)
               self.messageDisplay.releaseInterlock()

   def cancelCurrentJob(self, event):
       print repr(event.__dict__)
       if ( self.currentJob is not None and self.messageDisplay.getInterlock() == '1' ):
          if self.errorcb is not None:
             self.errorcb("Canceling Print Job...")
          print 'User canceled job', self.currentJob.jobId
          self.conn.cancelJob(self.currentJob.jobId, purge_job=True)

   def releaseJob(self, job, requestId):

       self.currentJob = job
       sub = self.conn.createSubscription(self.privateUri, ['all'], lease_duration=630)
       if self.messageDisplay.messageFrame is None:
          print "a messageFrame MUST be registered with the MessageDisplay prior to job release"
          return
       else:
          mf = self.messageDisplay.messageFrame

       conn = self.conn
       timeout = -1
       check = -1

       # define a function to clean up in the event of a job hanging on the printer
       def jobTimeout():
           print "entering jobTimeout"
           mf.after_cancel(check)
           try:
              confirm = filter(lambda ev: (ev['notify-subscribed-event'] == 'job-completed' and
                                           ev['notify-job-id'] == job.jobId), conn.getNotifications([sub])['events'])
              if len(confirm) == 0:
                 conn.cancelJob(job.jobId, purge_job=True)
              while len(confirm) == 0:
                 time.sleep(0.300);
                 confirm = filter(lambda ev: ( ev.has_key('notify-job-id') and ev.has_key('job-state') and
                                               ev['notify-job-id'] == job.jobId and ev['job-state'] > 6),
                                                                         conn.getNotifications([sub])['events'])
           except cups.IPPError:
               print 'caught an IPPError() while trying to print [%s]\n' % ( job.jobId, )
           finally:
               self.currentJob = None
               if self.messageDisplay.messageFrame is not None:
                  self.messageDisplay.messageFrame.event_generate('<<Finished>>')
                  self.messageDisplay.releaseInterlock()

       # define a private function to check the notifications
       def checkNotifications():
          try:
             notifications = conn.getNotifications([sub])
             print repr(notifications['events'])
             completed = filter(lambda ev: (ev['notify-subscribed-event'] == 'job-completed'), notifications['events'])
             completed = filter(lambda ev: (ev['notify-job-id'] == job.jobId), completed)
             if len(completed) == 0:
                mf.after(3000, checkNotifications)
             else:
                mf.after_cancel(timeout)
                conn.cancelSubscription(sub)
                self.currentJob = None
                if self.messageDisplay.messageFrame is not None:
                   self.messageDisplay.releaseInterlock()
                   self.messageDisplay.messageFrame.event_generate('<<Finished>>')

                # Check for media-sheets first from the job, then from the event, and finally
                # just assume that our query count was correct (some drivers do not properly provide
                # page count information
                if completed[-1]['job-state-reasons'] == 'job-completed-successfully':
                   attr = self.conn.getJobAttributes(job.jobId)
                   print >> sys.stderr, time(), "attr['job-media-sheets-completed']",attr['job-media-sheets-completed']
                   print >> sys.stderr, time(), "completed[-1]['job-impressions-completed']", completed[-1]['job-impressions-completed']

                   if attr['job-media-sheets-completed'] != 0:
                      sheetsCompleted = attr['job-media-sheets-completed']
                      print >> sys.stderr, time(), 'using job-media-sheets-completed: ', sheetsCompleted
                   elif completed[-1]['job-impressions-completed'] != 0:
                      sheetsCompleted = completed[-1]['job-impressions-completed']
                      if attr.has_key('Duplex') and attr['Duplex'] == u'DuplexNoTumble':
                         sheetsCompleted = ( sheetsCompleted  + (sheetsCompleted % 2) ) / 2
                      print >> sys.stderr, time(), 'using job-impressions-completed: ', sheetsCompleted
                   else:
                      sheetsCompleted = job.pages
                      print >> sys.stderr, time(), 'falling back to job.pages:', sheetsCompleted
                else:
                   sheetsCompleted = 0
                   print >> sys.stderr, time(), 'using sheetsCompleted = 0'

                self.pageAccounting(self.hostname, job.username, requestId, sheetsCompleted)
          except cups.IPPError:
                print "caught an IPPError"

       timeout = mf.after(600000, jobTimeout)
       check = mf.after(4000, checkNotifications)
       try:
          self.conn.moveJob(job_id=job.jobId, job_printer_uri=self.privateUri)
       except cups.IPPError:
          print 'print IPPError: \n'
          self.messageDisplay.releaseInterlock()

   def pageAccounting(self, hostname, username, requestId, sheets):
       url = '/atg/PageServer/deduct/%s/%s/%s/%d' % (hostname, username, requestId, sheets)
       http = httplib.HTTPSConnection(self.servername)
       http.request("GET", url)
       resp = http.getresponse()
       print resp.status, resp.reason
       xml = resp.read()
       root = ET.fromstring(xml)
       self.messageDisplay.quota(root, 'deductResponse')
       self.messageDisplay.bulletin(root, 'bulletin')
<commit_msg>standardized import of time to agree with other files and fix the time.sleep reference<commit_after>import Tkinter
import cups
import re
import httplib
import sys
import time
import xml.etree.ElementTree as ET

usernameKey = 'job-originating-user-name'
clientKey =   'job-originating-host-name'
titleKey = 'job-name'
uuidKey  = 'job-uuid'
supportedUriKey = 'printer-uri-supported'

class PageServerAuth(object):
   def __init__(self, private, hostname, idGenerator, display, conn):
       self.hostname   = hostname
       self.servername = 'wwwapp.cc.columbia.edu'
       self.idGenerator = idGenerator
       self.messageDisplay = display
       self.conn = conn
       printers = self.conn.getPrinters()
       self.privateUri = printers[private][supportedUriKey]
       self.currentJob = None				       # Support for event Handlers
       self.errorcb = None                                     # Support for event Handlers

   def authorizeJobs(self, selectedJobs, errorcb, authname):
       requestId=self.idGenerator()
       self.errorcb = errorcb

       for j in selectedJobs:
          self.messageDisplay.claimInterlock()
          url = '/atg/PageServer/query/%s/%s/%.5x/%d' % (self.hostname, authname, requestId, j.pages)
          http = httplib.HTTPSConnection(self.servername)
          http.request("GET", url)
          resp = http.getresponse()
          xml = resp.read()
          root = ET.fromstring(xml)
          queryResponse = root.find('queryResponse').attrib
          if queryResponse['Authorized'] == 'True':
               # reset the username on any authorized unclaimed job
               if j.username != authname:
                  j.username = authname
               self.releaseJob(j, queryResponse['reqId'])
          else:
               errorcb('Insufficient Quota')
               self.messageDisplay.quota(root, 'queryResponse', True)
               self.messageDisplay.releaseInterlock()

   def cancelCurrentJob(self, event):
       print repr(event.__dict__)
       if ( self.currentJob is not None and self.messageDisplay.getInterlock() == '1' ):
          if self.errorcb is not None:
             self.errorcb("Canceling Print Job...")
          print 'User canceled job', self.currentJob.jobId
          self.conn.cancelJob(self.currentJob.jobId, purge_job=True)

   def releaseJob(self, job, requestId):

       self.currentJob = job
       sub = self.conn.createSubscription(self.privateUri, ['all'], lease_duration=630)
       if self.messageDisplay.messageFrame is None:
          print "a messageFrame MUST be registered with the MessageDisplay prior to job release"
          return
       else:
          mf = self.messageDisplay.messageFrame

       conn = self.conn
       timeout = -1
       check = -1

       # define a function to clean up in the event of a job hanging on the printer
       def jobTimeout():
           print "entering jobTimeout"
           mf.after_cancel(check)
           try:
              confirm = filter(lambda ev: (ev['notify-subscribed-event'] == 'job-completed' and
                                           ev['notify-job-id'] == job.jobId), conn.getNotifications([sub])['events'])
              if len(confirm) == 0:
                 conn.cancelJob(job.jobId, purge_job=True)
              while len(confirm) == 0:
                 time.sleep(0.300);
                 confirm = filter(lambda ev: ( ev.has_key('notify-job-id') and ev.has_key('job-state') and
                                               ev['notify-job-id'] == job.jobId and ev['job-state'] > 6),
                                                                         conn.getNotifications([sub])['events'])
           except cups.IPPError:
               print 'caught an IPPError() while trying to print [%s]\n' % ( job.jobId, )
           finally:
               self.currentJob = None
               if self.messageDisplay.messageFrame is not None:
                  self.messageDisplay.messageFrame.event_generate('<<Finished>>')
                  self.messageDisplay.releaseInterlock()

       # define a private function to check the notifications
       def checkNotifications():
          try:
             notifications = conn.getNotifications([sub])
             print repr(notifications['events'])
             completed = filter(lambda ev: (ev['notify-subscribed-event'] == 'job-completed'), notifications['events'])
             completed = filter(lambda ev: (ev['notify-job-id'] == job.jobId), completed)
             if len(completed) == 0:
                mf.after(3000, checkNotifications)
             else:
                mf.after_cancel(timeout)
                conn.cancelSubscription(sub)
                self.currentJob = None
                if self.messageDisplay.messageFrame is not None:
                   self.messageDisplay.releaseInterlock()
                   self.messageDisplay.messageFrame.event_generate('<<Finished>>')

                # Check for media-sheets first from the job, then from the event, and finally
                # just assume that our query count was correct (some drivers do not properly provide
                # page count information
                if completed[-1]['job-state-reasons'] == 'job-completed-successfully':
                   attr = self.conn.getJobAttributes(job.jobId)
                   print >> sys.stderr, time.time(), "attr['job-media-sheets-completed']",attr['job-media-sheets-completed']
                   print >> sys.stderr, time.time(), "completed[-1]['job-impressions-completed']", completed[-1]['job-impressions-completed']

                   if attr['job-media-sheets-completed'] != 0:
                      sheetsCompleted = attr['job-media-sheets-completed']
                      print >> sys.stderr, time.time(), 'using job-media-sheets-completed: ', sheetsCompleted
                   elif completed[-1]['job-impressions-completed'] != 0:
                      sheetsCompleted = completed[-1]['job-impressions-completed']
                      if attr.has_key('Duplex') and attr['Duplex'] == u'DuplexNoTumble':
                         sheetsCompleted = ( sheetsCompleted  + (sheetsCompleted % 2) ) / 2
                      print >> sys.stderr, time.time(), 'using job-impressions-completed: ', sheetsCompleted
                   else:
                      sheetsCompleted = job.pages
                      print >> sys.stderr, time.time(), 'falling back to job.pages:', sheetsCompleted
                else:
                   sheetsCompleted = 0
                   print >> sys.stderr, time.time(), 'using sheetsCompleted = 0'

                self.pageAccounting(self.hostname, job.username, requestId, sheetsCompleted)
          except cups.IPPError:
                print "caught an IPPError"

       timeout = mf.after(600000, jobTimeout)
       check = mf.after(4000, checkNotifications)
       try:
          self.conn.moveJob(job_id=job.jobId, job_printer_uri=self.privateUri)
       except cups.IPPError:
          print 'print IPPError: \n'
          self.messageDisplay.releaseInterlock()

   def pageAccounting(self, hostname, username, requestId, sheets):
       url = '/atg/PageServer/deduct/%s/%s/%s/%d' % (hostname, username, requestId, sheets)
       http = httplib.HTTPSConnection(self.servername)
       http.request("GET", url)
       resp = http.getresponse()
       print resp.status, resp.reason
       xml = resp.read()
       root = ET.fromstring(xml)
       self.messageDisplay.quota(root, 'deductResponse')
       self.messageDisplay.bulletin(root, 'bulletin')
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response, redirect
from django.http import HttpResponseForbidden
from django.core.mail import send_mail

from caracole import settings
from .. import models as m
from .getters import get_network, get_subgroup, get_candidacy
from .decorators import sg_admin_required

@login_required()
def candidacy(request):
    """Generate a page to choose and request candidacy among the legal ones."""
    user = request.user
    user_of_subgroups = m.Subgroup.objects.filter(users__in=[user])
    candidacies = m.Candidacy.objects.filter(user=user)
    # name, user_of, candidate_to, can_be_candidate_to
    networks = []
    for nw in m.Network.objects.all():
        sg_u = user_of_subgroups.filter(network=nw)
        cd = candidacies.filter(subgroup__network=nw)
        item = {'name': nw.name,
                'user_of': sg_u.first() if sg_u.exists() else None,
                'candidate_to': cd.first() if cd.exists() else None,
                'can_be_candidate_to': nw.subgroup_set.all()}
        if item['user_of']:
            item['can_be_candidate_to'] = item['can_be_candidate_to'].exclude(id=item['user_of'].id)
        if item['candidate_to']:
            item['can_be_candidate_to'] = item['can_be_candidate_to'].exclude(id=item['candidate_to'].user.id)
        networks.append(item)
    print networks
    return render_to_response('candidacy.html', {'user': user, 'networks': networks})

@login_required()
def leave_network(request, network):
    """Leave subgroups of this network, as a user and a subgroup admin (not as a network-admin)."""
    user = request.user
    nw = get_network(network)
    m.JournalEntry.log(user, "Left network %s", nw.name)
    for sg in user.user_of_subgroup.filter(network__id=nw.id):
        sg.users.remove(user.id)
    for sg in user.staff_of_subgroup.filter(network__id=nw.id):
        sg.staff.remove(user.id)

    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

@login_required()
def create_candidacy(request, subgroup):
    """Create the candidacy or act immediately if no validation is needed."""
    user = request.user
    sg = get_subgroup(subgroup)
    if not user.user_of_subgroup.filter(id=sg.id).exists(): # No candidacy for a group you already belong to
        # Remove any pending candidacy for a subgroup of the same network
        conflicting_candidacies = m.Candidacy.objects.filter(user__id=user.id, subgroup__network__id=sg.network.id)
        conflicting_candidacies.delete()
        cd = m.Candidacy.objects.create(user=user, subgroup=sg)
        if auto_validate_candidacy(cd):
            # TODO when should confirmation e-mails be avoided? shouldn't it be up to auto_validate_candidacy to decide?
            m.JournalEntry.log(user, "Applied for %s/%s, automatically granted", sg.network.name, sg.name)
            validate_candidacy_without_checking(request, candidacy=cd.id, response='Y', send_confirmation_mail=True)
        else:
            m.JournalEntry.log(user, "Applied for %s/%s, candidacy pending", sg.network.name, sg.name)
    else:
        m.JournalEntry.log(user, "Applied for %s/%s, but was already a member", sg.network.name, sg.name)

    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

def auto_validate_candidacy(cd):
    """Return True if a candidacy should be automatically granted."""
    if cd.subgroup.network.staff.filter(id=cd.user_id).exists():  # network-admin requests are automatically granted
        return True
    if (cd.subgroup.auto_validate or cd.subgroup.network.auto_validate) and\
            m.Subgroup.objects.filter(users=cd.user).exists():
        # Even if the subgroup is marked as auto-accepting, users who haven't been ever accepted in any subgroup
        # Should be manually accepted, to avoid bogus sign-ups.
        return True
    return False

@login_required()
def cancel_candidacy(request, candidacy):
    """Cancel your own, yet-unapproved candidacy."""
    user = request.user
    cd = get_candidacy(candidacy)
    if user != cd.user:
        return HttpResponseForbidden(u"Vous ne pouvez annuler que vos propres candidatures.")
    m.JournalEntry.log(user, "Cancelled own application for %s/%s", cd.subgroup.network.name, cd.subgroup.name)
    cd.delete()
    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

@sg_admin_required(lambda a: get_candidacy(a['candidacy']).subgroup)
def validate_candidacy(request, candidacy, response):
    cd = get_candidacy(candidacy)
    m.JournalEntry.log(request.user, "%s candidacy from %s to %s/%s",
                       ("Granted" if response == 'Y' else 'Rejected'), cd.user.username,
                       cd.subgroup.network.name, cd.subgroup.name)
    return validate_candidacy_without_checking(request, candidacy=candidacy, response=response, send_confirmation_mail=True)

def validate_candidacy_without_checking(request, candidacy, response, send_confirmation_mail):
    """A (legal) candidacy has been answered by an admin.
    Perform corresponding membership changes and notify user through e-mail."""
    cd = get_candidacy(candidacy)
    adm = request.user
    adm = u"%s %s (%s)" % (adm.first_name, adm.last_name, adm.email)
    mail = [u"Bonjour %s, \n\n" % (cd.user.first_name,)]
    if response == 'Y':
        prev_subgroups = cd.user.user_of_subgroup.filter(network__id=cd.subgroup.network.id)
        if prev_subgroups.exists():
            prev_sg = prev_subgroups.first()  # Normally there's only one
            was_sg_admin = prev_sg.staff.filter(id=cd.user_id).exists()
            prev_sg.users.remove(cd.user)
            if was_sg_admin:
                prev_sg.staff.remove(cd.user)
            mail += u"Votre transfert du sous-groupe %s au sous-groupe %s, " % (prev_sg.name, cd.subgroup.name)
        else:
            mail += u"Votre adhésion au sous-groupe %s, " % (cd.subgroup.name,)
            was_sg_admin = False
        mail += u"au sein du réseau %s, a été acceptée" % (cd.subgroup.network.name,)
        mail += u" par %s. " % (adm,) if adm else u" automatiquement. "
        cd.subgroup.users.add(cd.user)
        is_nw_admin = cd.subgroup.network.staff.filter(id=cd.user_id).exists()
        if was_sg_admin and is_nw_admin:
            cd.subgroup.staff.add(cd.user)
            mail += u"Vous êtes également nommé co-administrateur du sous-groupe %s." % (cd.subgroup.name,)
        mail += "\n\n"
        if cd.subgroup.network.delivery_set.filter(state=m.Delivery.ORDERING_ALL).exists():
            mail += u"Une commande est actuellement en cours, dépêchez vous de vous connecter sur le site pour y participer !"
        else:
            mail += u"Vos responsables de sous-groupe vous préviendront par mail quand une nouvelle commande sera ouverte."
    elif adm:  # Negative response from an admin
        mail += u"Votre demande d'adhésion au sous-groupe %s du réseau %s a été refusée par %s. " \
                u"Si cette décision vous surprend, ou vous semble injustifiée, veuillez entrer en contact par " \
                u"e-mail avec cette personne pour clarifier la situation." % (
            cd.subgroup.name, cd.subgroup.network.name, adm)
    else:  # Automatic refusal. Shouldn't happen in the system's current state.
        mail += u"Votre demande d'adhésion au sous-groupe %s du réseau %s a été refusée automatiquement." \
                u"Si cette décision vous surprend, contactez les administrateurs du réseau: %s" % (
            cd.subgroup.name, cd.subgroup.network.name,
            u", ".join(cd.subgroup.network.staff.all()),)

    mail += u"\n\nCordialement, le robot du site de commande des Circuits Courts Caracole."
    title = u"[caracole] Votre demande d'inscription au circuit court "+cd.subgroup.network.name
    if send_confirmation_mail:
        send_mail(subject=title, message=''.join(mail), from_email=settings.EMAIL_HOST_USER, recipient_list=[cd.user.email],
                  fail_silently=True)
    cd.delete()

    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

<commit_msg>redirection bugfix<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response, redirect
from django.http import HttpResponseForbidden
from django.core.mail import send_mail

from caracole import settings
from .. import models as m
from .getters import get_network, get_subgroup, get_candidacy
from .decorators import sg_admin_required

@login_required()
def candidacy(request):
    """Generate a page to choose and request candidacy among the legal ones."""
    user = request.user
    user_of_subgroups = m.Subgroup.objects.filter(users__in=[user])
    candidacies = m.Candidacy.objects.filter(user=user)
    # name, user_of, candidate_to, can_be_candidate_to
    networks = []
    for nw in m.Network.objects.all():
        sg_u = user_of_subgroups.filter(network=nw)
        cd = candidacies.filter(subgroup__network=nw)
        item = {'name': nw.name,
                'user_of': sg_u.first() if sg_u.exists() else None,
                'candidate_to': cd.first() if cd.exists() else None,
                'can_be_candidate_to': nw.subgroup_set.all()}
        if item['user_of']:
            item['can_be_candidate_to'] = item['can_be_candidate_to'].exclude(id=item['user_of'].id)
        if item['candidate_to']:
            item['can_be_candidate_to'] = item['can_be_candidate_to'].exclude(id=item['candidate_to'].user.id)
        networks.append(item)
    print networks
    return render_to_response('candidacy.html', {'user': user, 'networks': networks})

@login_required()
def leave_network(request, network):
    """Leave subgroups of this network, as a user and a subgroup admin (not as a network-admin)."""
    user = request.user
    nw = get_network(network)
    m.JournalEntry.log(user, "Left network %s", nw.name)
    for sg in user.user_of_subgroup.filter(network__id=nw.id):
        sg.users.remove(user.id)
    for sg in user.staff_of_subgroup.filter(network__id=nw.id):
        sg.staff.remove(user.id)

    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

@login_required()
def create_candidacy(request, subgroup):
    """Create the candidacy or act immediately if no validation is needed."""
    user = request.user
    sg = get_subgroup(subgroup)
    if not user.user_of_subgroup.filter(id=sg.id).exists(): # No candidacy for a group you already belong to
        # Remove any pending candidacy for a subgroup of the same network
        conflicting_candidacies = m.Candidacy.objects.filter(user__id=user.id, subgroup__network__id=sg.network.id)
        conflicting_candidacies.delete()
        cd = m.Candidacy.objects.create(user=user, subgroup=sg)
        if auto_validate_candidacy(cd):
            # TODO when should confirmation e-mails be avoided? shouldn't it be up to auto_validate_candidacy to decide?
            m.JournalEntry.log(user, "Applied for %s/%s, automatically granted", sg.network.name, sg.name)
            validate_candidacy_without_checking(request, candidacy=cd.id, response='Y', send_confirmation_mail=True)
        else:
            m.JournalEntry.log(user, "Applied for %s/%s, candidacy pending", sg.network.name, sg.name)
    else:
        m.JournalEntry.log(user, "Applied for %s/%s, but was already a member", sg.network.name, sg.name)

    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

def auto_validate_candidacy(cd):
    """Return True if a candidacy should be automatically granted."""
    if cd.subgroup.network.staff.filter(id=cd.user_id).exists():  # network-admin requests are automatically granted
        return True
    if (cd.subgroup.auto_validate or cd.subgroup.network.auto_validate) and\
            m.Subgroup.objects.filter(users=cd.user).exists():
        # Even if the subgroup is marked as auto-accepting, users who haven't been ever accepted in any subgroup
        # Should be manually accepted, to avoid bogus sign-ups.
        return True
    return False

@login_required()
def cancel_candidacy(request, candidacy):
    """Cancel your own, yet-unapproved candidacy."""
    user = request.user
    cd = get_candidacy(candidacy)
    if user != cd.user:
        return HttpResponseForbidden(u"Vous ne pouvez annuler que vos propres candidatures.")
    m.JournalEntry.log(user, "Cancelled own application for %s/%s", cd.subgroup.network.name, cd.subgroup.name)
    cd.delete()
    target = request.REQUEST.get('next', False)
    return redirect(target) if target else redirect('candidacy')

@sg_admin_required(lambda a: get_candidacy(a['candidacy']).subgroup)
def validate_candidacy(request, candidacy, response):
    cd = get_candidacy(candidacy)
    m.JournalEntry.log(request.user, "%s candidacy from %s to %s/%s",
                       ("Granted" if response == 'Y' else 'Rejected'), cd.user.username,
                       cd.subgroup.network.name, cd.subgroup.name)
    return validate_candidacy_without_checking(request, candidacy=candidacy, response=response, send_confirmation_mail=True)

def validate_candidacy_without_checking(request, candidacy, response, send_confirmation_mail):
    """A (legal) candidacy has been answered by an admin.
    Perform corresponding membership changes and notify user through e-mail."""
    cd = get_candidacy(candidacy)
    adm = request.user
    adm = u"%s %s (%s)" % (adm.first_name, adm.last_name, adm.email)
    mail = [u"Bonjour %s, \n\n" % (cd.user.first_name,)]
    if response == 'Y':
        prev_subgroups = cd.user.user_of_subgroup.filter(network__id=cd.subgroup.network.id)
        if prev_subgroups.exists():
            prev_sg = prev_subgroups.first()  # Normally there's only one
            was_sg_admin = prev_sg.staff.filter(id=cd.user_id).exists()
            prev_sg.users.remove(cd.user)
            if was_sg_admin:
                prev_sg.staff.remove(cd.user)
            mail += u"Votre transfert du sous-groupe %s au sous-groupe %s, " % (prev_sg.name, cd.subgroup.name)
        else:
            mail += u"Votre adhésion au sous-groupe %s, " % (cd.subgroup.name,)
            was_sg_admin = False
        mail += u"au sein du réseau %s, a été acceptée" % (cd.subgroup.network.name,)
        mail += u" par %s. " % (adm,) if adm else u" automatiquement. "
        cd.subgroup.users.add(cd.user)
        is_nw_admin = cd.subgroup.network.staff.filter(id=cd.user_id).exists()
        if was_sg_admin and is_nw_admin:
            cd.subgroup.staff.add(cd.user)
            mail += u"Vous êtes également nommé co-administrateur du sous-groupe %s." % (cd.subgroup.name,)
        mail += "\n\n"
        if cd.subgroup.network.delivery_set.filter(state=m.Delivery.ORDERING_ALL).exists():
            mail += u"Une commande est actuellement en cours, dépêchez vous de vous connecter sur le site pour y participer !"
        else:
            mail += u"Vos responsables de sous-groupe vous préviendront par mail quand une nouvelle commande sera ouverte."
    elif adm:  # Negative response from an admin
        mail += u"Votre demande d'adhésion au sous-groupe %s du réseau %s a été refusée par %s. " \
                u"Si cette décision vous surprend, ou vous semble injustifiée, veuillez entrer en contact par " \
                u"e-mail avec cette personne pour clarifier la situation." % (
            cd.subgroup.name, cd.subgroup.network.name, adm)
    else:  # Automatic refusal. Shouldn't happen in the system's current state.
        mail += u"Votre demande d'adhésion au sous-groupe %s du réseau %s a été refusée automatiquement." \
                u"Si cette décision vous surprend, contactez les administrateurs du réseau: %s" % (
            cd.subgroup.name, cd.subgroup.network.name,
            u", ".join(cd.subgroup.network.staff.all()),)

    mail += u"\n\nCordialement, le robot du site de commande des Circuits Courts Caracole."
    title = u"[caracole] Votre demande d'inscription au circuit court "+cd.subgroup.network.name
    if send_confirmation_mail:
        send_mail(subject=title, message=''.join(mail), from_email=settings.EMAIL_HOST_USER, recipient_list=[cd.user.email],
                  fail_silently=True)
    cd.delete()

    target = request.GET.get('next', False)
    return redirect(target) if target else redirect('candidacy')

<|endoftext|>