<commit_before>"""
.. module:: controller
   :synopsis: Basic controller classes for asynchronous optimization.
.. moduleauthor:: David Bindel <bindel@cornell.edu>
"""

try:
    import Queue
except ImportError:
    import queue as Queue

import heapq
import random
import threading
from poap.strategy import EvalRecord

class Controller(object):
    """Base class for controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        fevals: Database of function evaluations.
        feval_callbacks: List of callbacks to execute on new eval record
        term_callbacks: List of callbacks to execute on termination
    """

    def __init__(self):
        "Initialize the controller."
        self.strategy = None
        self.fevals = []
        self.feval_callbacks = []
        self.term_callbacks = []

    def add_timer(self, timeout, callback):
        "Add a task to be executed after a timeout (e.g. for monitoring)."
        thread = threading.Timer(timeout, callback)
        thread.start()

    def ping(self):
        "Tell controller to consult strategies when possible (if asynchronous)"
        pass

    def can_work(self):
        "Return whether we can currently perform work."
        return True

    def best_point(self):
        "Return the best point in the database."
        fcomplete = [f for f in self.fevals if f.status == 'completed']
        if fcomplete:
            return min(fcomplete, key=lambda x: x.value)

    def new_feval(self, params, status='pending'):
        """Add a function evaluation record to the database.
        """
        record = EvalRecord(params, status=status)
        self.fevals.append(record)
        for callback in self.feval_callbacks:
            callback(record)
        return record

    def call_term_callbacks(self):
        "Call termination callbacks."
        for callback in self.term_callbacks:
            callback()

    def add_term_callback(self, callback):
        "Add a callback for cleanup on termination."
        self.term_callbacks.append(callback)

    def add_feval_callback(self, callback):
        "Add a callback for notification on new fevals."
        self.feval_callbacks.append(callback)

    def remove_term_callback(self, callback):
        "Remove a callback from the term callback list."
        self.term_callbacks = [
            c for c in self.term_callbacks if c != callback
        ]

    def remove_feval_callback(self, callback):
        "Remove a callback from the feval callback list."
        self.feval_callbacks = [
            c for c in self.feval_callbacks if c != callback
        ]

class SerialController(Controller):
    """Serial optimization controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        objective: Objective function
        fevals: Database of function evaluations
    """

    def __init__(self, objective):
        "Initialize the controller."
        Controller.__init__(self)
        self.objective = objective

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            proposal = self.strategy.propose_action()
            if not proposal:
                raise NameError('No proposed action')
            if proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval':
                proposal.record = self.new_feval(proposal.args)
                proposal.accept()
                value = self.objective(*proposal.record.params)
                proposal.record.complete(value)
            elif proposal.action == 'kill':
                proposal.reject()
            else:
                proposal.reject()

    def run(self):
        "Run the optimization and return the best value."
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class ThreadController(Controller):
    """Thread-based optimization controller.

    The optimizer dispatches work to a queue of workers.
    Each worker has methods of the form

       worker.eval(record)
       worker.kill(record)

    These methods are asynchronous: they start a function evaluation
    or termination, but do not necessarily complete it.  The worker
    must respond to eval requests, but may ignore kill requests.  On
    eval requests, the worker should either attempt the evaluation or
    mark the record as killed.  The worker sends status updates back
    to the controller in terms of lambdas (executed at the controller)
    that update the relevant record.  When the worker becomes
    available again, it should use add_worker to add itself back to
    the queue.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        fevals: Database of function evaluations
        workers: Queue of available worker threads
        messages: Queue of messages from workers

    """

    def __init__(self):
        "Initialize the controller."
        Controller.__init__(self)
        self.workers = Queue.Queue()
        self.messages = Queue.Queue()
        self.io_lock = threading.Lock()

    def ping(self):
        "Tell controller to consult strategies when possible"
        self.add_message()

    def lprint(self, *args):
        "Locking I/O."
        self.io_lock.acquire()
        print("{0}".format(*args))
        self.io_lock.release()

    def add_timer(self, timeout, callback):
        "Add a task to be executed after a timeout (e.g. for monitoring)."
        thread = threading.Timer(timeout, lambda: self.add_message(callback))
        thread.start()

    def add_message(self, message=None):
        "Queue up a message."
        if message is None:
            self.messages.put(lambda: None)
        else:
            self.messages.put(message)

    def add_worker(self, worker):
        "Add a worker and queue a 'wake-up' message."
        self.workers.put(worker)
        self.add_message()

    def launch_worker(self, worker, daemon=False):
        "Launch and take ownership of a new worker thread."
        self.add_worker(worker)
        self.add_term_callback(worker.terminate)
        worker.daemon = worker.daemon or daemon
        worker.start()

    def can_work(self):
        "Claim we can work if a worker is available."
        return not self.workers.empty()

    def _submit_work(self, proposal):
        "Submit proposed work."
        try:
            worker = self.workers.get_nowait()
            proposal.record = self.new_feval(proposal.args)
            proposal.record.worker = worker
            proposal.accept()
            worker.eval(proposal.record)
        except Queue.Empty:
            proposal.reject()

    def _run_message(self):
        "Process a message, blocking for one if none is available."
        message = self.messages.get()
        message()

    def _run_queued_messages(self):
        "Process any queued messages."
        while not self.messages.empty():
            self._run_message()

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            self._run_queued_messages()
            proposal = self.strategy.propose_action()
            if not proposal:
                self._run_message()
            elif proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval' and self.can_work():
                self._submit_work(proposal)
            elif proposal.action == 'kill' and not proposal.args[0].is_done():
                record = proposal.args[0]
                record.worker.kill(record)
            else:
                proposal.reject()

    def run(self):
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class BaseWorkerThread(threading.Thread):
    """Worker base class for use with the thread controller.

    The BaseWorkerThread class has a run routine that actually handles
    the worker event loop, and a set of helper routines for
    dispatching messages into the worker event loop (usually from the
    controller) and dispatching messages to the controller (usually
    from the worker).
    """

    def __init__(self, controller):
        "Initialize the worker."
        super(BaseWorkerThread, self).__init__()
        self.controller = controller
        self.queue = Queue.Queue()

    def eval(self, record):
        "Start evaluation."
        self.queue.put(('eval', record))

    def kill(self, record):
        "Send kill message to worker."
        self.queue.put(('kill', record))

    def terminate(self):
        "Send termination message to worker."
        self.queue.put(('terminate',))

    def lprint(self, *args):
        "Print log message at controller"
        self.controller.lprint(*args)

    def add_message(self, message):
        "Send message to be executed at the controller."
        self.controller.add_message(message)

    def add_worker(self):
        "Add worker back to the work queue."
        self.controller.add_worker(self)

    def finish_success(self, record, value):
        "Finish successful work on a record and add ourselves back."
        self.add_message(lambda: record.complete(value))
        self.add_worker()

    def finish_failure(self, record):
        "Finish recording failure on a record and add ourselves back."
        self.add_message(record.kill)
        self.add_worker()

    def handle_eval(self, record):
        "Process an eval request."
        pass

    def handle_kill(self, record):
        "Process a kill request"
        pass

    def handle_terminate(self):
        "Handle any cleanup on a terminate request"
        pass

    def run(self):
        "Run requests as long as we get them."
        while True:
            request = self.queue.get()
            if request[0] == 'eval':
                record = request[1]
                self.add_message(record.running)
                self.handle_eval(record)
            elif request[0] == 'kill':
                self.handle_kill(request[1])
            elif request[0] == 'terminate':
                self.handle_terminate()
                return

class BasicWorkerThread(BaseWorkerThread):
    """Basic worker for use with the thread controller.

    The BasicWorkerThread calls a Python objective function
    when asked to do an evaluation.  This is concurrent, but only
    results in parallelism if the objective function implementation
    itself allows parallelism (e.g. because it communicates with
    an external entity via a pipe, socket, or whatever).
    """

    def __init__(self, controller, objective):
        "Initialize the worker."
        super(BasicWorkerThread, self).__init__(controller)
        self.objective = objective

    def handle_eval(self, record):
        self.finish_success(record, self.objective(*record.params))

class ProcessWorkerThread(BaseWorkerThread):
    """Subprocess worker for use with the thread controller.

    The ProcessWorkerThread is meant for use as a base class.
    Implementations that inherit from ProcessWorkerThread should
    define a handle_eval method that sets the process field so that it
    can be interrupted if needed.  This allows use of blocking
    communication primitives while at the same time allowing
    interruption.
    """

    def __init__(self, controller):
        "Initialize the worker."
        super(ProcessWorkerThread, self).__init__(controller)
        self.process = None

    def kill(self, record):
        "Send kill message."
        if self.process is not None and self.process.poll() is None:
            self.process.terminate()
        super(ProcessWorkerThread, self).kill(record)

    def terminate(self):
        "Send termination message."
        if self.process is not None and self.process.poll() is None:
            self.process.terminate()
        super(ProcessWorkerThread, self).terminate()
        self.join()

class SimTeamController(Controller):
    """Simulated parallel optimization controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        objective: Objective function
        delay: Time delay function
        workers: Number of workers available
        fevals: Database of function evaluations
        time: Current simulated time
        time_events: Time-stamped event heap
    """

    def __init__(self, objective, delay, workers):
        "Initialize the controller."
        Controller.__init__(self)
        self.objective = objective
        self.delay = delay
        self.workers = workers
        self.time = 0
        self.time_events = []

    def can_work(self):
        "Check if there are workers available."
        return self.workers > 0

    def submit_work(self, proposal):
        "Submit a work event."
        self.workers = self.workers-1
        record = self.new_feval(proposal.args)
        proposal.record = record
        proposal.accept()

        def event():
            "Closure for marking record done at some later point."
            if not record.is_done():
                record.complete(self.objective(*record.params))
                self.workers = self.workers + 1

        self.add_timer(self.delay(), event)

    def kill_work(self, proposal):
        "Submit a kill event."
        record = proposal.args[0]

        def event():
            """Closure for canceling a function evaluation
            NB: This is a separate event because it will eventually have delay!
            """
            if not record.is_done():
                record.kill()
                self.workers = self.workers + 1

        self.add_timer(0, event)

    def advance_time(self):
        "Advance time to the next event."
        assert self.time_events, "Deadlock detected!"
        time, event = heapq.heappop(self.time_events)
        self.time = time
        event()

    def add_timer(self, timeout, event):
        "Add new timer event."
        heapq.heappush(self.time_events, (self.time + timeout, event))

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            proposal = self.strategy.propose_action()
            if not proposal:
                self.advance_time()
            elif proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval' and self.can_work():
                self.submit_work(proposal)
            elif proposal.action == 'kill' and not proposal.args[0].is_done():
                self.kill_work(proposal)
            else:
                proposal.reject()
                self.advance_time()

    def run(self):
        "Run the optimization and return the best value."
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class Monitor(object):
    """Monitor events observed by a controller.

    The monitor object provides hooks to monitor the progress of an
    optimization run by a controller.  Users should inherit from Monitor
    and add custom version of the methods

        on_new_feval(self, record)
        on_update(self, record)
        on_terminate(self)
    """

    def __init__(self, controller):
        """Initialize the monitor.

        Args:
            controller: The controller whose fevals we will monitor
        """
        self.controller = controller
        controller.add_feval_callback(self._add_on_update)
        controller.add_feval_callback(self.on_new_feval)
        controller.add_term_callback(self.on_terminate)

    def _add_on_update(self, record):
        "Internal handler -- add on_update callback to all new fevals."
        record.add_callback(self.on_update)

    def on_new_feval(self, record):
        "Handle new function evaluation request."
        pass

    def on_update(self, record):
        "Handle function evaluation update."
        pass

    def on_terminate(self):
        "Handle termination."
        pass
<commit_msg>Killed random (previously used by ChaosMonkey).<commit_after>"""
.. module:: controller
   :synopsis: Basic controller classes for asynchronous optimization.
.. moduleauthor:: David Bindel <bindel@cornell.edu>
"""

try:
    import Queue
except ImportError:
    import queue as Queue

import heapq
import threading
from poap.strategy import EvalRecord

class Controller(object):
    """Base class for controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        fevals: Database of function evaluations.
        feval_callbacks: List of callbacks to execute on new eval record
        term_callbacks: List of callbacks to execute on termination
    """

    def __init__(self):
        "Initialize the controller."
        self.strategy = None
        self.fevals = []
        self.feval_callbacks = []
        self.term_callbacks = []

    def add_timer(self, timeout, callback):
        "Add a task to be executed after a timeout (e.g. for monitoring)."
        thread = threading.Timer(timeout, callback)
        thread.start()

    def ping(self):
        "Tell controller to consult strategies when possible (if asynchronous)"
        pass

    def can_work(self):
        "Return whether we can currently perform work."
        return True

    def best_point(self):
        "Return the best point in the database."
        fcomplete = [f for f in self.fevals if f.status == 'completed']
        if fcomplete:
            return min(fcomplete, key=lambda x: x.value)

    def new_feval(self, params, status='pending'):
        """Add a function evaluation record to the database.
        """
        record = EvalRecord(params, status=status)
        self.fevals.append(record)
        for callback in self.feval_callbacks:
            callback(record)
        return record

    def call_term_callbacks(self):
        "Call termination callbacks."
        for callback in self.term_callbacks:
            callback()

    def add_term_callback(self, callback):
        "Add a callback for cleanup on termination."
        self.term_callbacks.append(callback)

    def add_feval_callback(self, callback):
        "Add a callback for notification on new fevals."
        self.feval_callbacks.append(callback)

    def remove_term_callback(self, callback):
        "Remove a callback from the term callback list."
        self.term_callbacks = [
            c for c in self.term_callbacks if c != callback
        ]

    def remove_feval_callback(self, callback):
        "Remove a callback from the feval callback list."
        self.feval_callbacks = [
            c for c in self.feval_callbacks if c != callback
        ]

class SerialController(Controller):
    """Serial optimization controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        objective: Objective function
        fevals: Database of function evaluations
    """

    def __init__(self, objective):
        "Initialize the controller."
        Controller.__init__(self)
        self.objective = objective

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            proposal = self.strategy.propose_action()
            if not proposal:
                raise NameError('No proposed action')
            if proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval':
                proposal.record = self.new_feval(proposal.args)
                proposal.accept()
                value = self.objective(*proposal.record.params)
                proposal.record.complete(value)
            elif proposal.action == 'kill':
                proposal.reject()
            else:
                proposal.reject()

    def run(self):
        "Run the optimization and return the best value."
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class ThreadController(Controller):
    """Thread-based optimization controller.

    The optimizer dispatches work to a queue of workers.
    Each worker has methods of the form

       worker.eval(record)
       worker.kill(record)

    These methods are asynchronous: they start a function evaluation
    or termination, but do not necessarily complete it.  The worker
    must respond to eval requests, but may ignore kill requests.  On
    eval requests, the worker should either attempt the evaluation or
    mark the record as killed.  The worker sends status updates back
    to the controller in terms of lambdas (executed at the controller)
    that update the relevant record.  When the worker becomes
    available again, it should use add_worker to add itself back to
    the queue.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        fevals: Database of function evaluations
        workers: Queue of available worker threads
        messages: Queue of messages from workers

    """

    def __init__(self):
        "Initialize the controller."
        Controller.__init__(self)
        self.workers = Queue.Queue()
        self.messages = Queue.Queue()
        self.io_lock = threading.Lock()

    def ping(self):
        "Tell controller to consult strategies when possible"
        self.add_message()

    def lprint(self, *args):
        "Locking I/O."
        self.io_lock.acquire()
        print("{0}".format(*args))
        self.io_lock.release()

    def add_timer(self, timeout, callback):
        "Add a task to be executed after a timeout (e.g. for monitoring)."
        thread = threading.Timer(timeout, lambda: self.add_message(callback))
        thread.start()

    def add_message(self, message=None):
        "Queue up a message."
        if message is None:
            self.messages.put(lambda: None)
        else:
            self.messages.put(message)

    def add_worker(self, worker):
        "Add a worker and queue a 'wake-up' message."
        self.workers.put(worker)
        self.add_message()

    def launch_worker(self, worker, daemon=False):
        "Launch and take ownership of a new worker thread."
        self.add_worker(worker)
        self.add_term_callback(worker.terminate)
        worker.daemon = worker.daemon or daemon
        worker.start()

    def can_work(self):
        "Claim we can work if a worker is available."
        return not self.workers.empty()

    def _submit_work(self, proposal):
        "Submit proposed work."
        try:
            worker = self.workers.get_nowait()
            proposal.record = self.new_feval(proposal.args)
            proposal.record.worker = worker
            proposal.accept()
            worker.eval(proposal.record)
        except Queue.Empty:
            proposal.reject()

    def _run_message(self):
        "Process a message, blocking for one if none is available."
        message = self.messages.get()
        message()

    def _run_queued_messages(self):
        "Process any queued messages."
        while not self.messages.empty():
            self._run_message()

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            self._run_queued_messages()
            proposal = self.strategy.propose_action()
            if not proposal:
                self._run_message()
            elif proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval' and self.can_work():
                self._submit_work(proposal)
            elif proposal.action == 'kill' and not proposal.args[0].is_done():
                record = proposal.args[0]
                record.worker.kill(record)
            else:
                proposal.reject()

    def run(self):
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class BaseWorkerThread(threading.Thread):
    """Worker base class for use with the thread controller.

    The BaseWorkerThread class has a run routine that actually handles
    the worker event loop, and a set of helper routines for
    dispatching messages into the worker event loop (usually from the
    controller) and dispatching messages to the controller (usually
    from the worker).
    """

    def __init__(self, controller):
        "Initialize the worker."
        super(BaseWorkerThread, self).__init__()
        self.controller = controller
        self.queue = Queue.Queue()

    def eval(self, record):
        "Start evaluation."
        self.queue.put(('eval', record))

    def kill(self, record):
        "Send kill message to worker."
        self.queue.put(('kill', record))

    def terminate(self):
        "Send termination message to worker."
        self.queue.put(('terminate',))

    def lprint(self, *args):
        "Print log message at controller"
        self.controller.lprint(*args)

    def add_message(self, message):
        "Send message to be executed at the controller."
        self.controller.add_message(message)

    def add_worker(self):
        "Add worker back to the work queue."
        self.controller.add_worker(self)

    def finish_success(self, record, value):
        "Finish successful work on a record and add ourselves back."
        self.add_message(lambda: record.complete(value))
        self.add_worker()

    def finish_failure(self, record):
        "Finish recording failure on a record and add ourselves back."
        self.add_message(record.kill)
        self.add_worker()

    def handle_eval(self, record):
        "Process an eval request."
        pass

    def handle_kill(self, record):
        "Process a kill request"
        pass

    def handle_terminate(self):
        "Handle any cleanup on a terminate request"
        pass

    def run(self):
        "Run requests as long as we get them."
        while True:
            request = self.queue.get()
            if request[0] == 'eval':
                record = request[1]
                self.add_message(record.running)
                self.handle_eval(record)
            elif request[0] == 'kill':
                self.handle_kill(request[1])
            elif request[0] == 'terminate':
                self.handle_terminate()
                return

class BasicWorkerThread(BaseWorkerThread):
    """Basic worker for use with the thread controller.

    The BasicWorkerThread calls a Python objective function
    when asked to do an evaluation.  This is concurrent, but only
    results in parallelism if the objective function implementation
    itself allows parallelism (e.g. because it communicates with
    an external entity via a pipe, socket, or whatever).
    """

    def __init__(self, controller, objective):
        "Initialize the worker."
        super(BasicWorkerThread, self).__init__(controller)
        self.objective = objective

    def handle_eval(self, record):
        self.finish_success(record, self.objective(*record.params))

class ProcessWorkerThread(BaseWorkerThread):
    """Subprocess worker for use with the thread controller.

    The ProcessWorkerThread is meant for use as a base class.
    Implementations that inherit from ProcessWorkerThread should
    define a handle_eval method that sets the process field so that it
    can be interrupted if needed.  This allows use of blocking
    communication primitives while at the same time allowing
    interruption.
    """

    def __init__(self, controller):
        "Initialize the worker."
        super(ProcessWorkerThread, self).__init__(controller)
        self.process = None

    def kill(self, record):
        "Send kill message."
        if self.process is not None and self.process.poll() is None:
            self.process.terminate()
        super(ProcessWorkerThread, self).kill(record)

    def terminate(self):
        "Send termination message."
        if self.process is not None and self.process.poll() is None:
            self.process.terminate()
        super(ProcessWorkerThread, self).terminate()
        self.join()

class SimTeamController(Controller):
    """Simulated parallel optimization controller.

    Attributes:
        strategy: Strategy for choosing optimization actions.
        objective: Objective function
        delay: Time delay function
        workers: Number of workers available
        fevals: Database of function evaluations
        time: Current simulated time
        time_events: Time-stamped event heap
    """

    def __init__(self, objective, delay, workers):
        "Initialize the controller."
        Controller.__init__(self)
        self.objective = objective
        self.delay = delay
        self.workers = workers
        self.time = 0
        self.time_events = []

    def can_work(self):
        "Check if there are workers available."
        return self.workers > 0

    def submit_work(self, proposal):
        "Submit a work event."
        self.workers = self.workers-1
        record = self.new_feval(proposal.args)
        proposal.record = record
        proposal.accept()

        def event():
            "Closure for marking record done at some later point."
            if not record.is_done():
                record.complete(self.objective(*record.params))
                self.workers = self.workers + 1

        self.add_timer(self.delay(), event)

    def kill_work(self, proposal):
        "Submit a kill event."
        record = proposal.args[0]

        def event():
            """Closure for canceling a function evaluation
            NB: This is a separate event because it will eventually have delay!
            """
            if not record.is_done():
                record.kill()
                self.workers = self.workers + 1

        self.add_timer(0, event)

    def advance_time(self):
        "Advance time to the next event."
        assert self.time_events, "Deadlock detected!"
        time, event = heapq.heappop(self.time_events)
        self.time = time
        event()

    def add_timer(self, timeout, event):
        "Add new timer event."
        heapq.heappush(self.time_events, (self.time + timeout, event))

    def _run(self):
        "Run the optimization and return the best value."
        while True:
            proposal = self.strategy.propose_action()
            if not proposal:
                self.advance_time()
            elif proposal.action == 'terminate':
                proposal.accept()
                return self.best_point()
            elif proposal.action == 'eval' and self.can_work():
                self.submit_work(proposal)
            elif proposal.action == 'kill' and not proposal.args[0].is_done():
                self.kill_work(proposal)
            else:
                proposal.reject()
                self.advance_time()

    def run(self):
        "Run the optimization and return the best value."
        try:
            return self._run()
        finally:
            self.call_term_callbacks()

class Monitor(object):
    """Monitor events observed by a controller.

    The monitor object provides hooks to monitor the progress of an
    optimization run by a controller.  Users should inherit from Monitor
    and add custom version of the methods

        on_new_feval(self, record)
        on_update(self, record)
        on_terminate(self)
    """

    def __init__(self, controller):
        """Initialize the monitor.

        Args:
            controller: The controller whose fevals we will monitor
        """
        self.controller = controller
        controller.add_feval_callback(self._add_on_update)
        controller.add_feval_callback(self.on_new_feval)
        controller.add_term_callback(self.on_terminate)

    def _add_on_update(self, record):
        "Internal handler -- add on_update callback to all new fevals."
        record.add_callback(self.on_update)

    def on_new_feval(self, record):
        "Handle new function evaluation request."
        pass

    def on_update(self, record):
        "Handle function evaluation update."
        pass

    def on_terminate(self):
        "Handle termination."
        pass
<|endoftext|>


<commit_before># Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php

# Copyright 2006, Frank Scholz <coherence@beebits.net>

import os
import urllib, urlparse

from twisted.internet import task
from twisted.internet import reactor
from twisted.internet import threads
from twisted.web import xmlrpc, static
from twisted.web import resource, server
from twisted.python import log, failure, util

from elementtree.ElementTree import Element, SubElement, ElementTree, tostring

from coherence.upnp.services.servers.connection_manager_server import ConnectionManagerServer
from coherence.upnp.services.servers.content_directory_server import ContentDirectoryServer
from coherence.upnp.services.servers.media_receiver_registrar_server import MediaReceiverRegistrarServer
from coherence.upnp.services.servers.media_receiver_registrar_server import FakeMediaReceiverRegistrarBackend

from coherence.backends.fs_storage import FSStore
from coherence.backends.elisa_storage import ElisaMediaStore

class MSRoot(resource.Resource):

    def __init__(self, server, store):
        resource.Resource.__init__(self)
        self.server = server
        self.store = store

    def getChildWithDefault(self, path, request):
        print 'MSRoot %s getChildWithDefault' % self.server.device_type, path, request.uri, request.client
        headers = request.getAllHeaders()
        print 'headers', request.getAllHeaders()

        if( headers.has_key('user-agent') and
            headers['user-agent'].find('Xbox/') == 0 and
            path in ['description-1.xml','description-2.xml']):
            print 'XBox alert, we need to simulate a Windows Media Connect server'
            if self.children.has_key('xbox-description-1.xml'):
                print 'returning xbox-description-1.xml'
                return self.children['xbox-description-1.xml']

        if self.children.has_key(path):
            return self.children[path]
        if request.uri == '/':
            return self
        return self.getChild(path, request)

    def requestFinished(self, result, id):
        print "finished, remove %d from connection table" % id
        self.server.connection_manager_server.remove_connection(id)

    def getChild(self, name, request):
        print 'MSRoot getChild', name, request
        ch = self.store.get_by_id(name)
        if ch != None:
            p = ch.get_path()
            if os.path.exists(p):
                new_id = self.server.connection_manager_server.add_connection()
                print "startup, add %d to connection table" % new_id
                d = request.notifyFinish()
                d.addCallback(self.requestFinished, new_id)
                d.addErrback(self.requestFinished, new_id)
                ch = static.File(p)
        if ch is None:
            p = util.sibpath(__file__, name)
            if os.path.exists(p):
                ch = static.File(p)
        print 'MSRoot ch', ch
        return ch

    def listchilds(self, uri):
        print 'listchilds', uri
        cl = ''
        sep = ''
        if uri[-1] != '/':
            sep = '/'
        for c in self.children:
                cl += '
%s
' % (uri,sep,c,c)
        return cl

    def render(self,request):
        return '
root of the MediaServer

%s

'% self.listchilds(request.uri)

class RootDeviceXML(static.Data):

    def __init__(self, hostname, uuid, urlbase,
                        device_type='MediaServer',
                        version=2,
                        friendly_name='Coherence UPnP A/V MediaServer',
                        xbox_hack=False,
                        services=[],
                        devices=[]):
        uuid = str(uuid)
        root = Element('root')
        root.attrib['xmlns']='urn:schemas-upnp-org:device-1-0'
        device_type = 'urn:schemas-upnp-org:device:%s:%d' % (device_type, version)
        e = SubElement(root, 'specVersion')
        SubElement( e, 'major').text = '1'
        SubElement( e, 'minor').text = '0'

        SubElement(root, 'URLBase').text = urlbase

        d = SubElement(root, 'device')
        x = SubElement( d, 'dlna:X_DLNADOC')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'DMS-1.50'
        x = SubElement( d, 'dlna:X_DLNADOC')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'M-DMS-1.50'
        x=SubElement( d, 'dlna:X_DLNACAP')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'av-upload,image-upload,audio-upload'
        SubElement( d, 'deviceType').text = device_type
        if xbox_hack == False:
            SubElement( d, 'modelName').text = 'Coherence UPnP A/V MediaServer'
            SubElement( d, 'friendlyName').text = friendly_name
        else:
            SubElement( d, 'modelName').text = 'Windows Media Connect'
            SubElement( d, 'friendlyName').text = friendly_name + ' : 1 : Windows Media Connect'
        SubElement( d, 'manufacturer').text = 'beebits.net'
        SubElement( d, 'manufacturerURL').text = 'http://coherence.beebits.net'
        SubElement( d, 'modelDescription').text = 'Coherence UPnP A/V MediaServer'
        SubElement( d, 'modelNumber').text = '0.1'
        SubElement( d, 'modelURL').text = 'http://coherence.beebits.net'
        SubElement( d, 'serialNumber').text = '0000001'
        SubElement( d, 'UDN').text = uuid
        SubElement( d, 'UPC').text = ''
        SubElement( d, 'presentationURL').text = ''

        if len(services):
            e = SubElement( d, 'serviceList')
            for service in services:
                id = service.get_id()
                s = SubElement( e, 'service')
                try:
                    namespace = service.namespace
                except:
                    namespace = 'schemas-upnp-org'
                SubElement( s, 'serviceType').text = 'urn:%s:service:%s:%d' % (namespace, id, version)
                try:
                    namespace = service.namespace
                except:
                    namespace = 'upnp-org'
                SubElement( s, 'serviceId').text = 'urn:%s:serviceId:%s' % (namespace,id)
                if xbox_hack == False:
                    SubElement( s, 'SCPDURL').text = '/' + uuid[5:] + '/' + id + '/' + service.scpd_url
                    SubElement( s, 'controlURL').text = '/' + uuid[5:] + '/' + id + '/' + service.control_url
                    SubElement( s, 'eventSubURL').text = '/' + uuid[5:] + '/' + id + '/' + service.subscription_url
                else:
                    SubElement( s, 'SCPDURL').text = uuid[5:] + '/' + id + '/' + service.scpd_url
                    SubElement( s, 'controlURL').text = uuid[5:] + '/' + id + '/' + service.control_url
                    SubElement( s, 'eventSubURL').text = uuid[5:] + '/' + id + '/' + service.subscription_url

        if len(services):
            e = SubElement( d, 'deviceList')

        self.xml = tostring( root, encoding='utf-8')
        static.Data.__init__(self, self.xml, 'text/xml')

class MediaServer:

    def __init__(self, coherence, version=2):
        self.coherence = coherence
        self.device_type = 'MediaServer'
        self.version = version
        from coherence.upnp.core.uuid import UUID
        self.uuid = UUID()
        self.backend = None
        urlbase = self.coherence.urlbase
        if urlbase[-1] != '/':
            urlbase += '/'
        self.urlbase = urlbase + str(self.uuid)[5:]

        print 'MediaServer urlbase', urlbase

        p = 'tests/content'

        """ this could take some time, put it in a  thread to be sure it doesn't block
            as we can't tell for sure that every backend is implemented properly """
        d = threads.deferToThread(FSStore, 'my content', p, self.urlbase, (), self)
        #d = threads.deferToThread(ElisaMediaStore, 'Elisas content', 'localhost, self.urlbase, (), self)
        d.addCallback(self.backend_ready)
        d.addErrback(log.err)

    def backend_ready(self, backend):
        self._services = []
        self._devices = []

        self.backend = backend

        self.connection_manager_server = ConnectionManagerServer(self)
        self._services.append(self.connection_manager_server)

        self.content_directory_server = ContentDirectoryServer(self)
        self._services.append(self.content_directory_server)

        self.media_receiver_registrar_server = MediaReceiverRegistrarServer(self,
                                                        backend=FakeMediaReceiverRegistrarBackend())
        self._services.append(self.media_receiver_registrar_server)

        upnp_init = getattr(self.backend, "upnp_init", None)
        if upnp_init:
            upnp_init()

        self.web_resource = MSRoot( self, backend)
        self.coherence.add_web_resource( str(self.uuid)[5:], self.web_resource)

        version = self.version
        while version > 0:
            self.web_resource.putChild( 'description-%d.xml' % version,
                                    RootDeviceXML( self.coherence.hostname,
                                    str(self.uuid),
                                    self.coherence.urlbase,
                                    self.device_type, version,
                                    services=self._services,
                                    devices=self._devices))
            self.web_resource.putChild( 'xbox-description-%d.xml' % version,
                                    RootDeviceXML( self.coherence.hostname,
                                    str(self.uuid),
                                    self.coherence.urlbase,
                                    self.device_type, version,
                                    xbox_hack=True,
                                    services=self._services,
                                    devices=self._devices))
            version -= 1

        self.web_resource.putChild('ConnectionManager', self.connection_manager_server)
        self.web_resource.putChild('ContentDirectory', self.content_directory_server)
        self.web_resource.putChild('X_MS_MediaReceiverRegistrar', self.media_receiver_registrar_server)

        self.register()

    def register(self):
        s = self.coherence.ssdp_server
        uuid = str(self.uuid)
        #print '%s register' % self.device_type
        # we need to do this after the children are there, since we send notifies
        s.register('local',
                    '%s::upnp:rootdevice' % uuid,
                    'upnp:rootdevice',
                    self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version)

        s.register('local',
                    uuid,
                    uuid,
                    self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version)

        version = self.version
        while version > 0:
            s.register('local',
                        '%s::urn:schemas-upnp-org:device:%s:%d' % (uuid, self.device_type, version),
                        'urn:schemas-upnp-org:device:%s:%d' % (self.device_type, version),
                        self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % version)

            for service in self._services:
                try:
                    namespace = service.namespace
                except:
                    namespace = 'schemas-upnp-org'

                s.register('local',
                            '%s::urn:%s:service:%s:%d' % (uuid,namespace,service.id, version),
                            'urn:%s:service:%s:%d' % (namespace,service.id, version),
                            self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % version)

            version -= 1

<commit_msg>take back 2006-12-11T20:41:39Z!dev@coherence-project.org, as it is probably not needed for the XBox<commit_after># Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php

# Copyright 2006, Frank Scholz <coherence@beebits.net>

import os
import urllib, urlparse

from twisted.internet import task
from twisted.internet import reactor
from twisted.internet import threads
from twisted.web import xmlrpc, static
from twisted.web import resource, server
from twisted.python import log, failure, util

from elementtree.ElementTree import Element, SubElement, ElementTree, tostring

from coherence.upnp.services.servers.connection_manager_server import ConnectionManagerServer
from coherence.upnp.services.servers.content_directory_server import ContentDirectoryServer
from coherence.upnp.services.servers.media_receiver_registrar_server import MediaReceiverRegistrarServer
from coherence.upnp.services.servers.media_receiver_registrar_server import FakeMediaReceiverRegistrarBackend

from coherence.backends.fs_storage import FSStore
from coherence.backends.elisa_storage import ElisaMediaStore

class MSRoot(resource.Resource):

    def __init__(self, server, store):
        resource.Resource.__init__(self)
        self.server = server
        self.store = store

    def getChildWithDefault(self, path, request):
        print 'MSRoot %s getChildWithDefault' % self.server.device_type, path, request.uri, request.client
        headers = request.getAllHeaders()
        print 'headers', request.getAllHeaders()

        if( headers.has_key('user-agent') and
            headers['user-agent'].find('Xbox/') == 0 and
            path in ['description-1.xml','description-2.xml']):
            print 'XBox alert, we need to simulate a Windows Media Connect server'
            if self.children.has_key('xbox-description-1.xml'):
                print 'returning xbox-description-1.xml'
                return self.children['xbox-description-1.xml']

        if self.children.has_key(path):
            return self.children[path]
        if request.uri == '/':
            return self
        return self.getChild(path, request)

    def requestFinished(self, result, id):
        print "finished, remove %d from connection table" % id
        self.server.connection_manager_server.remove_connection(id)

    def getChild(self, name, request):
        print 'MSRoot getChild', name, request
        ch = self.store.get_by_id(name)
        if ch != None:
            p = ch.get_path()
            if os.path.exists(p):
                new_id = self.server.connection_manager_server.add_connection()
                print "startup, add %d to connection table" % new_id
                d = request.notifyFinish()
                d.addCallback(self.requestFinished, new_id)
                d.addErrback(self.requestFinished, new_id)
                ch = static.File(p)
        if ch is None:
            p = util.sibpath(__file__, name)
            if os.path.exists(p):
                ch = static.File(p)
        print 'MSRoot ch', ch
        return ch

    def listchilds(self, uri):
        print 'listchilds', uri
        cl = ''
        sep = ''
        if uri[-1] != '/':
            sep = '/'
        for c in self.children:
                cl += '
%s
' % (uri,sep,c,c)
        return cl

    def render(self,request):
        return '
root of the MediaServer

%s

'% self.listchilds(request.uri)

class RootDeviceXML(static.Data):

    def __init__(self, hostname, uuid, urlbase,
                        device_type='MediaServer',
                        version=2,
                        friendly_name='Coherence UPnP A/V MediaServer',
                        xbox_hack=False,
                        services=[],
                        devices=[]):
        uuid = str(uuid)
        root = Element('root')
        root.attrib['xmlns']='urn:schemas-upnp-org:device-1-0'
        device_type = 'urn:schemas-upnp-org:device:%s:%d' % (device_type, version)
        e = SubElement(root, 'specVersion')
        SubElement( e, 'major').text = '1'
        SubElement( e, 'minor').text = '0'

        SubElement(root, 'URLBase').text = urlbase

        d = SubElement(root, 'device')
        x = SubElement( d, 'dlna:X_DLNADOC')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'DMS-1.50'
        x = SubElement( d, 'dlna:X_DLNADOC')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'M-DMS-1.50'
        x=SubElement( d, 'dlna:X_DLNACAP')
        x.attrib['xmlns:dlna']='urn:schemas-dlna-org:device-1-0'
        x.text = 'av-upload,image-upload,audio-upload'
        SubElement( d, 'deviceType').text = device_type
        if xbox_hack == False:
            SubElement( d, 'modelName').text = 'Coherence UPnP A/V MediaServer'
            SubElement( d, 'friendlyName').text = friendly_name
        else:
            SubElement( d, 'modelName').text = 'Windows Media Connect'
            SubElement( d, 'friendlyName').text = friendly_name + ' : 1 : Windows Media Connect'
        SubElement( d, 'manufacturer').text = 'beebits.net'
        SubElement( d, 'manufacturerURL').text = 'http://coherence.beebits.net'
        SubElement( d, 'modelDescription').text = 'Coherence UPnP A/V MediaServer'
        SubElement( d, 'modelNumber').text = '0.1'
        SubElement( d, 'modelURL').text = 'http://coherence.beebits.net'
        SubElement( d, 'serialNumber').text = '0000001'
        SubElement( d, 'UDN').text = uuid
        SubElement( d, 'UPC').text = ''
        SubElement( d, 'presentationURL').text = ''

        if len(services):
            e = SubElement( d, 'serviceList')
            for service in services:
                id = service.get_id()
                s = SubElement( e, 'service')
                try:
                    namespace = service.namespace
                except:
                    namespace = 'schemas-upnp-org'
                SubElement( s, 'serviceType').text = 'urn:%s:service:%s:%d' % (namespace, id, version)
                try:
                    namespace = service.namespace
                except:
                    namespace = 'upnp-org'
                SubElement( s, 'serviceId').text = 'urn:%s:serviceId:%s' % (namespace,id)
                SubElement( s, 'SCPDURL').text = '/' + uuid[5:] + '/' + id + '/' + service.scpd_url
                SubElement( s, 'controlURL').text = '/' + uuid[5:] + '/' + id + '/' + service.control_url
                SubElement( s, 'eventSubURL').text = '/' + uuid[5:] + '/' + id + '/' + service.subscription_url

        if len(services):
            e = SubElement( d, 'deviceList')

        self.xml = tostring( root, encoding='utf-8')
        static.Data.__init__(self, self.xml, 'text/xml')

class MediaServer:

    def __init__(self, coherence, version=2):
        self.coherence = coherence
        self.device_type = 'MediaServer'
        self.version = version
        from coherence.upnp.core.uuid import UUID
        self.uuid = UUID()
        self.backend = None
        urlbase = self.coherence.urlbase
        if urlbase[-1] != '/':
            urlbase += '/'
        self.urlbase = urlbase + str(self.uuid)[5:]

        print 'MediaServer urlbase', urlbase

        p = 'tests/content'

        """ this could take some time, put it in a  thread to be sure it doesn't block
            as we can't tell for sure that every backend is implemented properly """
        d = threads.deferToThread(FSStore, 'my content', p, self.urlbase, (), self)
        #d = threads.deferToThread(ElisaMediaStore, 'Elisas content', 'localhost, self.urlbase, (), self)
        d.addCallback(self.backend_ready)
        d.addErrback(log.err)

    def backend_ready(self, backend):
        self._services = []
        self._devices = []

        self.backend = backend

        self.connection_manager_server = ConnectionManagerServer(self)
        self._services.append(self.connection_manager_server)

        self.content_directory_server = ContentDirectoryServer(self)
        self._services.append(self.content_directory_server)

        self.media_receiver_registrar_server = MediaReceiverRegistrarServer(self,
                                                        backend=FakeMediaReceiverRegistrarBackend())
        self._services.append(self.media_receiver_registrar_server)

        upnp_init = getattr(self.backend, "upnp_init", None)
        if upnp_init:
            upnp_init()

        self.web_resource = MSRoot( self, backend)
        self.coherence.add_web_resource( str(self.uuid)[5:], self.web_resource)

        version = self.version
        while version > 0:
            self.web_resource.putChild( 'description-%d.xml' % version,
                                    RootDeviceXML( self.coherence.hostname,
                                    str(self.uuid),
                                    self.coherence.urlbase,
                                    self.device_type, version,
                                    services=self._services,
                                    devices=self._devices))
            self.web_resource.putChild( 'xbox-description-%d.xml' % version,
                                    RootDeviceXML( self.coherence.hostname,
                                    str(self.uuid),
                                    self.coherence.urlbase,
                                    self.device_type, version,
                                    xbox_hack=True,
                                    services=self._services,
                                    devices=self._devices))
            version -= 1

        self.web_resource.putChild('ConnectionManager', self.connection_manager_server)
        self.web_resource.putChild('ContentDirectory', self.content_directory_server)
        self.web_resource.putChild('X_MS_MediaReceiverRegistrar', self.media_receiver_registrar_server)

        self.register()

    def register(self):
        s = self.coherence.ssdp_server
        uuid = str(self.uuid)
        #print '%s register' % self.device_type
        # we need to do this after the children are there, since we send notifies
        s.register('local',
                    '%s::upnp:rootdevice' % uuid,
                    'upnp:rootdevice',
                    self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version)

        s.register('local',
                    uuid,
                    uuid,
                    self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version)

        version = self.version
        while version > 0:
            s.register('local',
                        '%s::urn:schemas-upnp-org:device:%s:%d' % (uuid, self.device_type, version),
                        'urn:schemas-upnp-org:device:%s:%d' % (self.device_type, version),
                        self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % version)

            for service in self._services:
                try:
                    namespace = service.namespace
                except:
                    namespace = 'schemas-upnp-org'

                s.register('local',
                            '%s::urn:%s:service:%s:%d' % (uuid,namespace,service.id, version),
                            'urn:%s:service:%s:%d' % (namespace,service.id, version),
                            self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % version)

            version -= 1

<|endoftext|>


<commit_before>from django.views.i18n import set_language as django_set_language
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def set_language(request):
    return django_set_language(request)
<commit_msg>Language change fix<commit_after>from django.views.i18n import set_language as django_set_language
from django.views.decorators.csrf import csrf_exempt
from dark_lang import DARK_LANGUAGE_KEY
from django_locale.trans_real import LANGUAGE_SESSION_KEY
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from django import http

@csrf_exempt
def set_language(request):

    auth_user = request.user.is_authenticated()
    lang_code = request.POST.get('language', None)

    request.session[LANGUAGE_SESSION_KEY] = lang_code

    if auth_user:
        set_user_preference(request.user, DARK_LANGUAGE_KEY, lang_code)

    return django_set_language(request)
    # we can return a simple redirect response, but I left this in place just in case !<|endoftext|>


<commit_before>import math
from collections import namedtuple, defaultdict

from . import replays

PlayerPerformance = namedtuple('PlayerPerformance',
                               ['battle_count', 'avg_dmg', 'avg_kills', 'avg_spotted', 'survival_rate',
                                'avg_spot_damage', 'avg_pot_damage', 'win_rate', 'wn7', 'avg_decap',
                                'avg_tier'])

def player_performance(battles, players):
    """ Player statistics from replay files of given battles and players: damage done, spots, wn7, ... """
    battle_count = defaultdict(int)
    dmg = defaultdict(float)
    kills = defaultdict(int)
    survived = defaultdict(int)
    spotted = defaultdict(int)
    spot_damage = defaultdict(float)
    potential_damage = defaultdict(float)
    wins = defaultdict(int)
    decap = defaultdict(int)
    tier = defaultdict(int)
    for battle in battles:
        replay_data = battle.replay.unpickle()
        if not replay_data:
            continue

        if replay_data['first']['clientVersionFromExe'] in ('0, 8, 11 0', '0, 9, 0, 0'):
            players_perf = replays.player_performance(replay_data['second'], replay_data['second'][0]['vehicles'],
                                                      replay_data['second'][0]['players'])
        else:
            if not replay_data or not 'pickle' in replay_data or not replay_data['pickle']:
                continue
            if not isinstance(replay_data['pickle']['vehicles'], dict):
                continue
            players_perf = replays.player_performance(replay_data['second'], replay_data['pickle']['vehicles'],
                                                      replay_data['pickle']['players'])

        for player in battle.get_players():
            if not player in players:
                continue
            if not str(player.wot_id) in players_perf:
                # Replay/Players mismatch (account sharing?), skip
                continue
            perf = players_perf[str(player.wot_id)]
            battle_count[player] += 1
            dmg[player] += perf['damageDealt']
            spot_damage[player] += perf['damageAssistedRadio']
            kills[player] += perf['kills']
            survived[player] += 1 if perf['survived'] else 0
            potential_damage[player] += perf['potentialDamageReceived']
            wins[player] += 1 if battle.victory else 0
            spotted[player] += perf['spotted']
            decap[player] += perf['droppedCapturePoints']
            tier[player] += perf['tank_info']['tier']

    avg_dmg = defaultdict(float)
    avg_kills = defaultdict(float)
    survival_rate = defaultdict(float)
    avg_spotted = defaultdict(float)
    avg_spot_damage = defaultdict(float)
    avg_pot_damage = defaultdict(float)
    win_rate = defaultdict(float)
    avg_decap = defaultdict(float)
    avg_tier = defaultdict(float)
    for p in players:
        if battle_count[p] > 0:
            bc = float(battle_count[p])
            avg_dmg[p] = dmg[p] / bc
            avg_kills[p] = kills[p] / bc
            survival_rate[p] = survived[p] / bc
            avg_spotted[p] = spotted[p] / bc
            avg_spot_damage[p] = spot_damage[p] / bc
            avg_pot_damage[p] = potential_damage[p] / bc
            win_rate[p] = wins[p] / bc
            avg_decap[p] = decap[p] / bc
            avg_tier[p] = tier[p] / bc

    wn7 = defaultdict(float)
    for p in players:
        if battle_count[p] == 0:
            continue
        tier = avg_tier[p]
        wn7[p] = (1240.0 - 1040.0 / ((min(6, tier)) ** 0.164)) * avg_kills[p] \
                 + avg_dmg[p] * 530.0 / (184.0 * math.exp(0.24 * tier) + 130.0) \
                 + avg_spotted[p] * 125.0 * min(tier, 3) / 3.0 \
                 + min(avg_decap[p], 2.2) * 100.0 \
                 + ((185 / (0.17 + math.exp((win_rate[p] * 100.0 - 35.0) * -0.134))) - 500.0) * 0.45 \
                 - ((5.0 - min(tier, 5)) * 125.0) / (
            1.0 + math.exp(( tier - (battle_count[p] / 220.0) ** (3.0 / tier) ) * 1.5))

    result = PlayerPerformance(
        battle_count=battle_count,
        avg_dmg=avg_dmg,
        avg_kills=avg_kills,
        avg_spotted=avg_spotted,
        survival_rate=survival_rate,
        avg_spot_damage=avg_spot_damage,
        avg_pot_damage=avg_pot_damage,
        win_rate=win_rate,
        avg_decap=avg_decap,
        avg_tier=avg_tier,
        wn7=wn7
    )
    return result<commit_msg>Fix replay version detection<commit_after>import math
from collections import namedtuple, defaultdict

from . import replays

PlayerPerformance = namedtuple('PlayerPerformance',
                               ['battle_count', 'avg_dmg', 'avg_kills', 'avg_spotted', 'survival_rate',
                                'avg_spot_damage', 'avg_pot_damage', 'win_rate', 'wn7', 'avg_decap',
                                'avg_tier'])

def player_performance(battles, players):
    """ Player statistics from replay files of given battles and players: damage done, spots, wn7, ... """
    battle_count = defaultdict(int)
    dmg = defaultdict(float)
    kills = defaultdict(int)
    survived = defaultdict(int)
    spotted = defaultdict(int)
    spot_damage = defaultdict(float)
    potential_damage = defaultdict(float)
    wins = defaultdict(int)
    decap = defaultdict(int)
    tier = defaultdict(int)
    for battle in battles:
        replay_data = battle.replay.unpickle()
        if not replay_data:
            continue

        replay_version_tokens = replay_data['first']['clientVersionFromExe'].split(",")
        replay_major_version = int(replay_version_tokens[1])
        replay_minor_version = int(replay_version_tokens[2])
        if replay_major_version > 8 or (replay_major_version == 8 and replay_minor_version >= 11):
            players_perf = replays.player_performance(replay_data['second'], replay_data['second'][0]['vehicles'],
                                                      replay_data['second'][0]['players'])
        else:
            if not replay_data or not 'pickle' in replay_data or not replay_data['pickle']:
                continue
            if not isinstance(replay_data['pickle']['vehicles'], dict):
                continue
            players_perf = replays.player_performance(replay_data['second'], replay_data['pickle']['vehicles'],
                                                      replay_data['pickle']['players'])

        for player in battle.get_players():
            if not player in players:
                continue
            if not str(player.wot_id) in players_perf:
                # Replay/Players mismatch (account sharing?), skip
                continue
            perf = players_perf[str(player.wot_id)]
            battle_count[player] += 1
            dmg[player] += perf['damageDealt']
            spot_damage[player] += perf['damageAssistedRadio']
            kills[player] += perf['kills']
            survived[player] += 1 if perf['survived'] else 0
            potential_damage[player] += perf['potentialDamageReceived']
            wins[player] += 1 if battle.victory else 0
            spotted[player] += perf['spotted']
            decap[player] += perf['droppedCapturePoints']
            tier[player] += perf['tank_info']['tier']

    avg_dmg = defaultdict(float)
    avg_kills = defaultdict(float)
    survival_rate = defaultdict(float)
    avg_spotted = defaultdict(float)
    avg_spot_damage = defaultdict(float)
    avg_pot_damage = defaultdict(float)
    win_rate = defaultdict(float)
    avg_decap = defaultdict(float)
    avg_tier = defaultdict(float)
    for p in players:
        if battle_count[p] > 0:
            bc = float(battle_count[p])
            avg_dmg[p] = dmg[p] / bc
            avg_kills[p] = kills[p] / bc
            survival_rate[p] = survived[p] / bc
            avg_spotted[p] = spotted[p] / bc
            avg_spot_damage[p] = spot_damage[p] / bc
            avg_pot_damage[p] = potential_damage[p] / bc
            win_rate[p] = wins[p] / bc
            avg_decap[p] = decap[p] / bc
            avg_tier[p] = tier[p] / bc

    wn7 = defaultdict(float)
    for p in players:
        if battle_count[p] == 0:
            continue
        tier = avg_tier[p]
        wn7[p] = (1240.0 - 1040.0 / ((min(6, tier)) ** 0.164)) * avg_kills[p] \
                 + avg_dmg[p] * 530.0 / (184.0 * math.exp(0.24 * tier) + 130.0) \
                 + avg_spotted[p] * 125.0 * min(tier, 3) / 3.0 \
                 + min(avg_decap[p], 2.2) * 100.0 \
                 + ((185 / (0.17 + math.exp((win_rate[p] * 100.0 - 35.0) * -0.134))) - 500.0) * 0.45 \
                 - ((5.0 - min(tier, 5)) * 125.0) / (
            1.0 + math.exp(( tier - (battle_count[p] / 220.0) ** (3.0 / tier) ) * 1.5))

    result = PlayerPerformance(
        battle_count=battle_count,
        avg_dmg=avg_dmg,
        avg_kills=avg_kills,
        avg_spotted=avg_spotted,
        survival_rate=survival_rate,
        avg_spot_damage=avg_spot_damage,
        avg_pot_damage=avg_pot_damage,
        win_rate=win_rate,
        avg_decap=avg_decap,
        avg_tier=avg_tier,
        wn7=wn7
    )
    return result<|endoftext|>


<commit_before>import re
import urllib
import urllib2
import urlparse

from lxml import etree
from lxml.builder import E

from sharepoint.xml import SP, namespaces, OUT
from sharepoint.lists.types import type_mapping, default_type

uuid_re = re.compile(r'^\{?([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})\}?$')

LIST_WEBSERVICE = '_vti_bin/Lists.asmx'

# From http://msdn.microsoft.com/en-us/library/lists.lists.addlist%28v=office.12%29.aspx
LIST_TEMPLATES = {
    'Announcements': 104,
    'Contacts': 105,
    'Custom List': 100,
    'Custom List in Datasheet View': 120,
    'DataSources': 110,
    'Discussion Board': 108,
    'Document Library': 101,
    'Events': 106,
    'Form Library': 115,
    'Issues': 1100,
    'Links': 103,
    'Picture Library': 109,
    'Survey': 102,
    'Tasks': 107
}

class SharePointLists(object):
    def __init__(self, opener):
        self.opener = opener

    @property
    def all_lists(self):
        if not hasattr(self, '_all_lists'):
            xml = SP.GetListCollection()
            result = self.opener.post_soap(LIST_WEBSERVICE, xml)

            self._all_lists = []
            for list_element in result.xpath('sp:GetListCollectionResult/sp:Lists/sp:List', namespaces=namespaces):
                self._all_lists.append(SharePointList(self.opener, self, list_element))
        return self._all_lists

    def remove(self, list):
        """
        Removes a list from the site.
        """
        xml = SP.DeleteList(SP.listName(list.id))
        result = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                       soapaction='http://schemas.microsoft.com/sharepoint/soap/DeleteList')
        self.all_lists.remove(list)

    def create(self, name, description='', template=100):
        """
        Creates a new list in the site.
        """
        try:
            template = int(template)
        except ValueError:
            template = LIST_TEMPLATES[template]
        if name in self:
            raise ValueError("List already exists: '{0}".format(name))
        if uuid_re.match(name):
            raise ValueError("Cannot create a list with a UUID as a name")
        xml = SP.AddList(SP.listName(name),
                         SP.description(description),
                         SP.templateID(unicode(template)))
        result = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                       soapaction='http://schemas.microsoft.com/sharepoint/soap/AddList')
        list_element = result.xpath('sp:AddListResult/sp:List', namespaces=namespaces)[0]
        self._all_lists.append(SharePointList(self.opener, self, list_element))

    def __iter__(self):
        return iter(self.all_lists)

    def __getitem__(self, key):
        if isinstance(key, int):
            return self.all_lists[key]
        elif uuid_re.match(key.lower()):
            # Using group 1 and adding braces allows us to match IDs that
            # didn't originally have braces.
            key = '{' + '{0}'.format(uuid_re.match(key.lower()).group(1)) + '}'
            for list_object in self.all_lists:
                if list_object.id == key:
                    return list_object
            raise KeyError('No list with ID {0}'.format(key))
        elif isinstance(key, basestring):
            for list_object in self.all_lists:
                if list_object.meta['Title'] == key:
                    return list_object
            raise KeyError("No list with title '{0}'".format(key))
        raise KeyError

    def __contains__(self, key):
        try:
            self[key]
        except KeyError:
            return False
        else:
            return True

    def as_xml(self, keys=None, **kwargs):
        if keys is not None:
            lists = (self[key] for key in keys)
        else:
            lists = self
        return OUT.lists(*[l.as_xml(**kwargs) for l in lists])

class SharePointList(object):
    def __init__(self, opener, lists, settings):
        self.opener = opener
        self.lists = lists
        self._deleted_rows = set()
        self._settings, self._meta = settings, None
        self.id = self.meta['ID'].lower()

    def __repr__(self):
        return "<SharePointList {0} '{1}'>".format(self.id, self.meta['Title'])

    @property
    def name(self):
        return self.meta['Title']

    @property
    def meta(self):
        if not self._meta:
            settings = self._settings if self._settings is not None else self.settings
            self._meta = dict(settings.attrib)
        return self._meta

    @property
    def settings(self):
        if not self._settings or not len(self._settings):
            xml = SP.GetList(SP.listName(self.id))
            response = self.opener.post_soap(LIST_WEBSERVICE, xml)
            self._settings = response[0][0]
        return self._settings

    @property
    def rows(self):
        if not hasattr(self, '_rows'):
            xml = SP.GetListItems(SP.listName(self.id), SP.rowLimit("100000"))
            response = self.opener.post_soap(LIST_WEBSERVICE, xml)
            xml_rows = list(response[0][0][0])
            self._rows = []
            for xml_row in xml_rows:
                self._rows.append(self.Row(xml_row))
        return list(self._rows)

    @property
    def rows_by_id(self):
        if not hasattr(self, '_rows_by_id'):
            self._rows_by_id = {}
            for row in self.rows:
                self._rows_by_id[row.id] = row
        return self._rows_by_id

    @property
    def fields(self):
        if not hasattr(self, '_fields'):
            self._fields = []
            for field in self.settings.xpath('sp:Fields/sp:Field', namespaces=namespaces):
                field_class = type_mapping.get(field.attrib['Type'], default_type)
                self._fields.append(field_class(self.lists, self.id, field))
        return self._fields

    @property
    def Row(self):
        """
        The class for a row in this list.
        """
        if not hasattr(self, '_row_class'):
            attrs = {'fields': self.fields, 'list': self, 'opener': self.opener}
            for field in self.fields:
                attrs[field.name] = field.descriptor
            self._row_class = type('SharePointListRow', (SharePointListRow,), attrs)
        return self._row_class

    def as_xml(self, include_data=True, include_field_definitions=True, **kwargs):
        list_element = OUT('list', name=self.name, id=self.id)

        if include_field_definitions:
            fields_element = OUT('fields')
            for field in self.fields:
                field_element = OUT('field',
                                  name=field.name,
                                  display_name=field.display_name,
                                  sharepoint_type=field.sharepoint_type,
                                  type=field.type_name,
                                  **field.extra_field_definition())
                if field.description:
                    field_element.attrib['description'] = field.description
                field_element.attrib['multi'] = 'true' if field.multi else 'false'
                fields_element.append(field_element)
            list_element.append(fields_element)

        if include_data:
            rows_element = OUT('rows')
            for row in self.rows:
                rows_element.append(row.as_xml(**kwargs))
            list_element.append(rows_element)
        return list_element

    def append(self, row):
        """
        Appends a row to the list. Takes a dictionary, returns a row.
        """
        if isinstance(row, dict):
            row = self.Row(row)
        elif isinstance(row, self.Row):
            pass
        elif isinstance(row, SharePointListRow):
            raise TypeError("row must be a dict or an instance of SharePointList.Row, not SharePointListRow")
        else:
            raise TypeError("row must be a dict or an instance of SharePointList.Row")
        self._rows.append(row)
        return row

    def remove(self, row):
        """
        Removes the row from the list.
        """
        self._rows.remove(row)
        self._deleted_rows.add(row)

    def delete(self):
        """
        Deletes the list from the site.
        """
        self.lists.remove(self)

    def save(self):
        """
        Updates the list with changes.
        """
        # Based on the documentation at
        # http://msdn.microsoft.com/en-us/library/lists.lists.updatelistitems%28v=office.12%29.aspx

        # Note, this ends up un-namespaced. SharePoint doesn't care about
        # namespaces on this XML node, and will bork if any of these elements
        # have a namespace prefix. Likewise Method and Field in
        # SharePointRow.get_batch_method().
        batches = E.Batch(ListVersion='1', OnError='Continue')
        # Here's the root element of our SOAP request.
        xml = SP.UpdateListItems(SP.listName(self.id), SP.updates(batches))

        # rows_by_batch_id contains a mapping from new rows to their batch
        # IDs, so we can set their IDs when they are returned by SharePoint.
        rows_by_batch_id, batch_id = {}, 1

        for row in self._rows:
            batch = row.get_batch_method()
            if batch is None:
                continue
            # Add the batch ID
            batch.attrib['ID'] = unicode(batch_id)
            rows_by_batch_id[batch_id] = row
            batches.append(batch)
            batch_id += 1

        for row in self._deleted_rows:
            batch = E.Method(E.Field(unicode(row.id),
                                     Name='ID'),
                             ID=unicode(batch_id), Cmd='Delete')
            rows_by_batch_id[batch_id] = row
            batches.append(batch)
            batch_id += 1

        if len(batches) == 0:
            return

        response = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                         soapaction='http://schemas.microsoft.com/sharepoint/soap/UpdateListItems')

        for result in response.xpath('.//sp:Result', namespaces=namespaces):
            batch_id, batch_result = result.attrib['ID'].split(',')
            row = rows_by_batch_id[int(batch_id)]
            if batch_result in ('Update', 'New'):
                row._update(result.xpath('z:row', namespaces=namespaces)[0],
                            clear=True)
            else:
                self._deleted_rows.remove(row)

        assert not self._deleted_rows
        assert not any(row._changed for row in self.rows)

class SharePointListRow(object):
    # fields, list and opener are added as class attributes in SharePointList.Row

    def __init__(self, row=None):
        self._update(row, clear=True)

    def _update(self, row, clear=False):
        if clear:
            self._data = {}
            self._changed = set()
        if isinstance(row, dict):
            for key in row:
                setattr(self, key, row[key])
        elif isinstance(row, etree._Element):
            for field in self.fields:
                value = field.parse(row)
                if value is not None:
                    self._data[field.name] = value
        elif row is not None:
            raise TypeError("row should be a dict or etree._Element.")
        try:
            self.id = self.ID
        except AttributeError:
            self.id = None

    def __repr__(self):
        return "<SharePointListRow {0} '{1}'>".format(self.id, self.name)

    def delete(self):
        self.list.remove(self)

    @property
    def name(self):
        try:
            return self.Title
        except AttributeError:
            return self.LinkFilename

    def get_batch_method(self):
        """
        Returns a change batch for SharePoint's UpdateListItems operation.
        """
        if not self._changed:
            return None

        batch_method = E.Method(Cmd='Update' if self.id else 'New')
        batch_method.append(E.Field(unicode(self.id) if self.id else 'New',
                                    Name='ID'))
        for field in self.fields:
            if field.name in self._changed:
                value = field.unparse(self._data[field.name] or '')
                batch_method.append(E.Field(value, Name=field.name))
        return batch_method

    @property
    def is_file(self):
        return hasattr(self, 'LinkFilename')

    def as_xml(self, transclude_xml=False, **kwargs):
        fields_element = OUT('fields')
        row_element = OUT('row', fields_element, id=unicode(self.id))
        for field in self.fields:
            try:
                data = self._data[field.name]
            except KeyError:
                pass
            else:
                fields_element.append(field.as_xml(self, data, **kwargs))
        if transclude_xml and self.is_file and self._data.get('DocIcon') == 'xml':
            try:
                content = etree.parse(self.open()).getroot()
            except urllib2.HTTPError, e:
                content_element = OUT('content', missing='true')
            else:
                content_element = OUT('content', content)
            row_element.append(content_element)
        return row_element

    def open(self):
        url = self.opener.relative(self.list.meta['Title'] + '/' + urllib.quote(self.LinkFilename))
        request = urllib2.Request(url)
        request.add_header('Translate', 'f')
        return self.opener.open(request)
<commit_msg>Quell deprecation warning (bool(etree._Element)).<commit_after>import re
import urllib
import urllib2
import urlparse

from lxml import etree
from lxml.builder import E

from sharepoint.xml import SP, namespaces, OUT
from sharepoint.lists.types import type_mapping, default_type

uuid_re = re.compile(r'^\{?([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})\}?$')

LIST_WEBSERVICE = '_vti_bin/Lists.asmx'

# From http://msdn.microsoft.com/en-us/library/lists.lists.addlist%28v=office.12%29.aspx
LIST_TEMPLATES = {
    'Announcements': 104,
    'Contacts': 105,
    'Custom List': 100,
    'Custom List in Datasheet View': 120,
    'DataSources': 110,
    'Discussion Board': 108,
    'Document Library': 101,
    'Events': 106,
    'Form Library': 115,
    'Issues': 1100,
    'Links': 103,
    'Picture Library': 109,
    'Survey': 102,
    'Tasks': 107
}

class SharePointLists(object):
    def __init__(self, opener):
        self.opener = opener

    @property
    def all_lists(self):
        if not hasattr(self, '_all_lists'):
            xml = SP.GetListCollection()
            result = self.opener.post_soap(LIST_WEBSERVICE, xml)

            self._all_lists = []
            for list_element in result.xpath('sp:GetListCollectionResult/sp:Lists/sp:List', namespaces=namespaces):
                self._all_lists.append(SharePointList(self.opener, self, list_element))
        return self._all_lists

    def remove(self, list):
        """
        Removes a list from the site.
        """
        xml = SP.DeleteList(SP.listName(list.id))
        result = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                       soapaction='http://schemas.microsoft.com/sharepoint/soap/DeleteList')
        self.all_lists.remove(list)

    def create(self, name, description='', template=100):
        """
        Creates a new list in the site.
        """
        try:
            template = int(template)
        except ValueError:
            template = LIST_TEMPLATES[template]
        if name in self:
            raise ValueError("List already exists: '{0}".format(name))
        if uuid_re.match(name):
            raise ValueError("Cannot create a list with a UUID as a name")
        xml = SP.AddList(SP.listName(name),
                         SP.description(description),
                         SP.templateID(unicode(template)))
        result = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                       soapaction='http://schemas.microsoft.com/sharepoint/soap/AddList')
        list_element = result.xpath('sp:AddListResult/sp:List', namespaces=namespaces)[0]
        self._all_lists.append(SharePointList(self.opener, self, list_element))

    def __iter__(self):
        return iter(self.all_lists)

    def __getitem__(self, key):
        if isinstance(key, int):
            return self.all_lists[key]
        elif uuid_re.match(key.lower()):
            # Using group 1 and adding braces allows us to match IDs that
            # didn't originally have braces.
            key = '{' + '{0}'.format(uuid_re.match(key.lower()).group(1)) + '}'
            for list_object in self.all_lists:
                if list_object.id == key:
                    return list_object
            raise KeyError('No list with ID {0}'.format(key))
        elif isinstance(key, basestring):
            for list_object in self.all_lists:
                if list_object.meta['Title'] == key:
                    return list_object
            raise KeyError("No list with title '{0}'".format(key))
        raise KeyError

    def __contains__(self, key):
        try:
            self[key]
        except KeyError:
            return False
        else:
            return True

    def as_xml(self, keys=None, **kwargs):
        if keys is not None:
            lists = (self[key] for key in keys)
        else:
            lists = self
        return OUT.lists(*[l.as_xml(**kwargs) for l in lists])

class SharePointList(object):
    def __init__(self, opener, lists, settings):
        self.opener = opener
        self.lists = lists
        self._deleted_rows = set()
        self._settings, self._meta = settings, None
        self.id = self.meta['ID'].lower()

    def __repr__(self):
        return "<SharePointList {0} '{1}'>".format(self.id, self.meta['Title'])

    @property
    def name(self):
        return self.meta['Title']

    @property
    def meta(self):
        if not self._meta:
            settings = self._settings if self._settings is not None else self.settings
            self._meta = dict(settings.attrib)
        return self._meta

    @property
    def settings(self):
        if self._settings is None or not len(self._settings):
            xml = SP.GetList(SP.listName(self.id))
            response = self.opener.post_soap(LIST_WEBSERVICE, xml)
            self._settings = response[0][0]
        return self._settings

    @property
    def rows(self):
        if not hasattr(self, '_rows'):
            xml = SP.GetListItems(SP.listName(self.id), SP.rowLimit("100000"))
            response = self.opener.post_soap(LIST_WEBSERVICE, xml)
            xml_rows = list(response[0][0][0])
            self._rows = []
            for xml_row in xml_rows:
                self._rows.append(self.Row(xml_row))
        return list(self._rows)

    @property
    def rows_by_id(self):
        if not hasattr(self, '_rows_by_id'):
            self._rows_by_id = {}
            for row in self.rows:
                self._rows_by_id[row.id] = row
        return self._rows_by_id

    @property
    def fields(self):
        if not hasattr(self, '_fields'):
            self._fields = []
            for field in self.settings.xpath('sp:Fields/sp:Field', namespaces=namespaces):
                field_class = type_mapping.get(field.attrib['Type'], default_type)
                self._fields.append(field_class(self.lists, self.id, field))
        return self._fields

    @property
    def Row(self):
        """
        The class for a row in this list.
        """
        if not hasattr(self, '_row_class'):
            attrs = {'fields': self.fields, 'list': self, 'opener': self.opener}
            for field in self.fields:
                attrs[field.name] = field.descriptor
            self._row_class = type('SharePointListRow', (SharePointListRow,), attrs)
        return self._row_class

    def as_xml(self, include_data=True, include_field_definitions=True, **kwargs):
        list_element = OUT('list', name=self.name, id=self.id)

        if include_field_definitions:
            fields_element = OUT('fields')
            for field in self.fields:
                field_element = OUT('field',
                                  name=field.name,
                                  display_name=field.display_name,
                                  sharepoint_type=field.sharepoint_type,
                                  type=field.type_name,
                                  **field.extra_field_definition())
                if field.description:
                    field_element.attrib['description'] = field.description
                field_element.attrib['multi'] = 'true' if field.multi else 'false'
                fields_element.append(field_element)
            list_element.append(fields_element)

        if include_data:
            rows_element = OUT('rows')
            for row in self.rows:
                rows_element.append(row.as_xml(**kwargs))
            list_element.append(rows_element)
        return list_element

    def append(self, row):
        """
        Appends a row to the list. Takes a dictionary, returns a row.
        """
        if isinstance(row, dict):
            row = self.Row(row)
        elif isinstance(row, self.Row):
            pass
        elif isinstance(row, SharePointListRow):
            raise TypeError("row must be a dict or an instance of SharePointList.Row, not SharePointListRow")
        else:
            raise TypeError("row must be a dict or an instance of SharePointList.Row")
        self._rows.append(row)
        return row

    def remove(self, row):
        """
        Removes the row from the list.
        """
        self._rows.remove(row)
        self._deleted_rows.add(row)

    def delete(self):
        """
        Deletes the list from the site.
        """
        self.lists.remove(self)

    def save(self):
        """
        Updates the list with changes.
        """
        # Based on the documentation at
        # http://msdn.microsoft.com/en-us/library/lists.lists.updatelistitems%28v=office.12%29.aspx

        # Note, this ends up un-namespaced. SharePoint doesn't care about
        # namespaces on this XML node, and will bork if any of these elements
        # have a namespace prefix. Likewise Method and Field in
        # SharePointRow.get_batch_method().
        batches = E.Batch(ListVersion='1', OnError='Continue')
        # Here's the root element of our SOAP request.
        xml = SP.UpdateListItems(SP.listName(self.id), SP.updates(batches))

        # rows_by_batch_id contains a mapping from new rows to their batch
        # IDs, so we can set their IDs when they are returned by SharePoint.
        rows_by_batch_id, batch_id = {}, 1

        for row in self._rows:
            batch = row.get_batch_method()
            if batch is None:
                continue
            # Add the batch ID
            batch.attrib['ID'] = unicode(batch_id)
            rows_by_batch_id[batch_id] = row
            batches.append(batch)
            batch_id += 1

        for row in self._deleted_rows:
            batch = E.Method(E.Field(unicode(row.id),
                                     Name='ID'),
                             ID=unicode(batch_id), Cmd='Delete')
            rows_by_batch_id[batch_id] = row
            batches.append(batch)
            batch_id += 1

        if len(batches) == 0:
            return

        response = self.opener.post_soap(LIST_WEBSERVICE, xml,
                                         soapaction='http://schemas.microsoft.com/sharepoint/soap/UpdateListItems')

        for result in response.xpath('.//sp:Result', namespaces=namespaces):
            batch_id, batch_result = result.attrib['ID'].split(',')
            row = rows_by_batch_id[int(batch_id)]
            if batch_result in ('Update', 'New'):
                row._update(result.xpath('z:row', namespaces=namespaces)[0],
                            clear=True)
            else:
                self._deleted_rows.remove(row)

        assert not self._deleted_rows
        assert not any(row._changed for row in self.rows)

class SharePointListRow(object):
    # fields, list and opener are added as class attributes in SharePointList.Row

    def __init__(self, row=None):
        self._update(row, clear=True)

    def _update(self, row, clear=False):
        if clear:
            self._data = {}
            self._changed = set()
        if isinstance(row, dict):
            for key in row:
                setattr(self, key, row[key])
        elif isinstance(row, etree._Element):
            for field in self.fields:
                value = field.parse(row)
                if value is not None:
                    self._data[field.name] = value
        elif row is not None:
            raise TypeError("row should be a dict or etree._Element.")
        try:
            self.id = self.ID
        except AttributeError:
            self.id = None

    def __repr__(self):
        return "<SharePointListRow {0} '{1}'>".format(self.id, self.name)

    def delete(self):
        self.list.remove(self)

    @property
    def name(self):
        try:
            return self.Title
        except AttributeError:
            return self.LinkFilename

    def get_batch_method(self):
        """
        Returns a change batch for SharePoint's UpdateListItems operation.
        """
        if not self._changed:
            return None

        batch_method = E.Method(Cmd='Update' if self.id else 'New')
        batch_method.append(E.Field(unicode(self.id) if self.id else 'New',
                                    Name='ID'))
        for field in self.fields:
            if field.name in self._changed:
                value = field.unparse(self._data[field.name] or '')
                batch_method.append(E.Field(value, Name=field.name))
        return batch_method

    @property
    def is_file(self):
        return hasattr(self, 'LinkFilename')

    def as_xml(self, transclude_xml=False, **kwargs):
        fields_element = OUT('fields')
        row_element = OUT('row', fields_element, id=unicode(self.id))
        for field in self.fields:
            try:
                data = self._data[field.name]
            except KeyError:
                pass
            else:
                fields_element.append(field.as_xml(self, data, **kwargs))
        if transclude_xml and self.is_file and self._data.get('DocIcon') == 'xml':
            try:
                content = etree.parse(self.open()).getroot()
            except urllib2.HTTPError, e:
                content_element = OUT('content', missing='true')
            else:
                content_element = OUT('content', content)
            row_element.append(content_element)
        return row_element

    def open(self):
        url = self.opener.relative(self.list.meta['Title'] + '/' + urllib.quote(self.LinkFilename))
        request = urllib2.Request(url)
        request.add_header('Translate', 'f')
        return self.opener.open(request)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from base import Command

class Version(Command):
    """
    Displays software version
    """

    keyword = u'version'
    information_message = u'Display software version.'

    def execute(self, arguments=None):
        """
        Displays software version
        """
        self.shell.say(u"{} version {}".format(self.shell.name,
                                               self.shell.version))
<commit_msg>mask the 'version' command to streamline the interface<commit_after># -*- coding: utf-8 -*-

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from base import Command

class Version(Command):
    """
    Displays software version
    """

    keyword = u'version'
    information_message = u'Display software version.'
    is_hidden = True

    def execute(self, arguments=None):
        """
        Displays software version
        """
        self.shell.say(u"{} version {}".format(self.shell.name,
                                               self.shell.version))
<|endoftext|>