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

"""
    File: __writer.py
    Description: This module implements the logic of a Writer.

    A Writer is responsible to collect messages from the internal
    queue and deliver it to somewhere.

    In order to implement your Writer, you must extend this class
    and write 2 methods, like this:

    ==========
    from __writer import Writer

    class MyWriter(Writer):
        def setup(self):
            ... # Provide here whatever initializations are necessary.
            ... # Properties defined in a conf.yaml will be available
            ... # here as instance variables (e.g., self.foo)

            ... # You may define a list of required confs, e.g.:
            ... self.required_confs = ['foo', 'bar']
            ... # If they are not found in your conf, an exception is raised

        def write(self, msg):
            ... # Your code goes here.
            ... # You must return a boolean indicating whether the
            ... # message was delivered correctly or not.
    ==========

    There are basically 2 flavors of Writer: asynchronous and synchronous.

    Synchronous Writers are defined with a 'period' in conf.yaml
    Asynchronous Writers are defined WITHOUT a 'period' in conf.yaml.

    For synchronous Writers, write() is called after each 'period'.
    For asynchronous Writers, write() is called as soon a message arrives in
    the queue.
"""

import os
import time
import pickle
import logging
import threading

import helpers.kronos as kronos
from __exceptions import ConfigurationError

class Writer(threading.Thread):
    def __init__(self,
                 queue,                   # source of messages to be written
                 conf={},                 # additional configurations
                 blockable=True,          # stops if a message was not delivered
                 interval=None,           # interval to read from queue
                 retry_interval=1,        # retry interval (in seconds) to writing
                 retry_timeout=None,      # if timeout is reached, discard message
                 checkpoint_enabled=False,# deafult is to not deal with checkpoints
                 checkpoint_interval=60   # interval of checkpoint writing
                 ):
        self.log = logging.getLogger()
        self.conf = conf
        self.processed = 0
        self.discarded = 0
        self.queue = queue
        self.blocked = False
        self.interval = interval
        self.blockable = blockable
        self.retry_timeout = retry_timeout
        self.retry_interval = retry_interval
        self.checkpoint_enabled = checkpoint_enabled
        self.set_conf(conf)

        if self.checkpoint_enabled:
            self.last_checkpoint = self._read_checkpoint()
            if not hasattr(self, 'checkpoint_interval'):
                self.checkpoint_interval = checkpoint_interval
            if not hasattr(self, 'checkpoint_path'):
                self.log.error('Error. Please, configure a checkpoint_path')
                self.log.info('Aborting.')
                exit(-1)

        self.setup()

        if hasattr(self, 'required_confs'):
            self.validate_conf()

        self.scheduler = kronos.ThreadedScheduler()
        self.schedule_tasks()
        if self.checkpoint_enabled:
            self.schedule_checkpoint_writing()

        threading.Thread.__init__(self)

    def validate_conf(self):
        """Validate if required confs are present.
           required_confs are supposed to be set in setup() method.
        """
        for item in self.required_confs:
            if not hasattr(self, item):
                self.log.error('%s not defined in conf.yaml' % item)
                self.log.info('Aborting')
                raise ConfigurationError()

    def schedule_checkpoint_writing(self):
        self.scheduler.add_interval_task(self._write_checkpoint,
                                         "checkpoint writing",
                                         0,
                                         self.checkpoint_interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def _read_checkpoint(self):
        """Read checkpoint file from disk."""
        try:
            if not os.path.exists(self.checkpoint_path):
                self.log.info('No checkpoint found in %s.' % self.checkpoint_path)
                return ''
            f = open(self.checkpoint_path, 'rb')
            read = pickle.load(f)
            f.close()
            if read:
                return read
            else:
                return ''
            self.log.info("Checkpoint read from %s" % self.checkpoint_path)
        except Exception, e:
            self.log.error('Error reading checkpoint in %s' % self.checkpoint_path)
            self.log.error(e)

    def _write_checkpoint(self):
        """Write checkpoint in disk."""
        try:
            lc = self.last_checkpoint
            f = open(self.checkpoint_path, 'w')
            pickle.dump(lc, f)
            f.close()
            self.log.info('Checkpoint written: %s' % lc)
        except Exception, e:
            self.log.error('Error writing checkpoint in %s' % self.checkpoint_path)
            self.log.error(e)

    def set_conf(self, conf):
        """Turns configuration properties into instance properties."""
        try:
            for item in conf:
                if isinstance(conf[item], str):
                    exec("self.%s = '%s'" % (item, conf[item]))
                else:
                    exec("self.%s = %s" % (item, conf[item]))
            self.log.info("Configuration settings added with success into writer.")
        except Exception, e:
            self.log.error("Invalid configuration item: %s" % item)
            self.log.error(e)

    def reschedule_tasks(self):
        try:
            self.scheduler = kronos.ThreadedScheduler()
            self.schedule_tasks()
            if self.checkpoint_enabled:
                self.schedule_checkpoint_writing()
            self.scheduler.start()
            self.log.info("Success in rescheduling")
        except Exception, e:
            self.log.error("Error while rescheduling tasks")
            self.log.error(e)

    def schedule_tasks(self):
        try:
            if self.interval:
                self.schedule_interval_task()
            else:
                self.schedule_single_task()
        except Exception, e:
            self.log.error("Error while scheduling tasks")
            self.log.error(e)

    def schedule_single_task(self):
        self.scheduler.add_single_task(self.async_process,
                                       "single task",
                                       0,
                                       kronos.method.threaded,
                                       [],
                                       None)

    def schedule_interval_task(self):
        self.scheduler.add_interval_task(self.process,
                                         "periodic task",
                                         0,
                                         self.interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def retry_writing(self, msg):
        """Blocks writer till a message is written or a timeout is reached"""
        wrote = False
        time_passed = 0
        while True:
            self.log.info("Trying to rewrite message...")
            if self._write(msg.content):
                wrote = True
                self.processed += 1
                self.log.debug("Message written: %s" % msg)
                self.log.info("Rewriting done with success.")
                break
            elif self.retry_timeout and \
                 time_passed > self.retry_timeout:
                 self.discarded += 1
                 self.log.debug("Message: %s discarded due to timeout" % msg)
                 self.log.info("Retry timeout reached. Discarding message...")
                 break
            time.sleep(self.retry_interval)
            time_passed += self.retry_interval
        return wrote

    def async_process(self):
        """Method that processes (write) a message.
           It waits for new messages from the queue,
           and as soon as one arrives, it tries to deliver it.
        """
        while True:
            try:
                #blocks if none
                msg = self.queue.get()
                wrote = False
                if not self._write(msg.content):
                    if self.blockable:
                        self.blocked = True
                        self.log.warning("Writer blocked.")
                        wrote = self.retry_writing(msg)
                        self.blocked = False
                        self.log.info("Writer unblocked.")
                    else:
                        self.discarded += 1
                        self.log.info("Since it's not blockable, discarding message: %s" % msg)
                else:
                    wrote = True
                    self.processed += 1
                    self.log.debug("Message written: %s" % msg)

                if wrote and self.checkpoint_enabled:
                    self._set_checkpoint(msg.checkpoint)

            except Exception, e:
                self.log.error("Couldn't process message")
                self.log.error(e)

    def process(self):
        """Method called to process (write) a message.
            It is called in the end of each interval
            in the case of a periodic task.
            If it's an async writer it is called by a Reader as
            a callback.  So, it may be called by subclasses.
        """
        try:
            if self.queue.qsize() > 0:
                msg = self.queue.get()
                wrote = False
                if not self._write(msg.content):
                    if self.blockable:
                        self.scheduler.stop()
                        self.blocked = True
                        self.log.warning("Writer blocked.")

                        time_passed = 0
                        while True:
                            self.log.info("Trying to rewrite message...")
                            if self._write(msg.content):
                                wrote = True
                                self.log.debug("Message written: %s" % msg)
                                self.log.info("Rewriting done with success.")
                                break
                            elif self.retry_timeout and \
                                 time_passed > self.retry_timeout:
                                self.discarded += 1
                                self.log.info("Retry timeout reached. Discarding message...")
                                break
                            time.sleep(self.retry_interval)
                            time_passed += self.retry_interval
                        self.blocked = False
                        self.log.info("Writer unblocked.")
                        self.reschedule_tasks()
                    else:
                        self.discarded += 1
                        self.log.info("Since it's not blockable, discarding message: %s" % msg)
                else:
                    wrote = True
                    self.log.debug("Message written: %s" % msg)

                if wrote:
                    self.processed += 1
                    if self.checkpoint_enabled:
                        self._set_checkpoint(msg.checkpoint)
            else:
                self.log.debug("No messages in the queue to write.")
        except Exception, e:
            self.log.error("Couldn't process message")
            self.log.error(e)

    def _write(self, msg):
        """Method that calls write method defined by subclasses.
        Shouldn't be called by subclasses."""
        try:
            return self.write(msg)
        except Exception, e:
            self.log.error("Can't write message: %s" % msg)
            self.log.error(e)
            return False

    def setup(self):
        """Subclasses should implement."""

    def write(self, msg):
        """Subclasses should implement."""

    def _set_checkpoint(self, checkpoint):
        try:
            self.last_checkpoint = checkpoint
            self.log.debug("Last checkpoint: %s" %checkpoint)
        except Exception, e:
            self.log.error('Error updating last_checkpoint')
            self.log.error(e)

    def run(self):
        """Starts the writer"""
        self.scheduler.start()

    def __str__(self):
        return str(self.__dict__)
<commit_msg>adding healthcheck logging now in writer<commit_after># -*- coding: utf-8 -*-

"""
    File: __writer.py
    Description: This module implements the logic of a Writer.

    A Writer is responsible to collect messages from the internal
    queue and deliver it to somewhere.

    In order to implement your Writer, you must extend this class
    and write 2 methods, like this:

    ==========
    from __writer import Writer

    class MyWriter(Writer):
        def setup(self):
            ... # Provide here whatever initializations are necessary.
            ... # Properties defined in a conf.yaml will be available
            ... # here as instance variables (e.g., self.foo)

            ... # You may define a list of required confs, e.g.:
            ... self.required_confs = ['foo', 'bar']
            ... # If they are not found in your conf, an exception is raised

        def write(self, msg):
            ... # Your code goes here.
            ... # You must return a boolean indicating whether the
            ... # message was delivered correctly or not.
    ==========

    There are basically 2 flavors of Writer: asynchronous and synchronous.

    Synchronous Writers are defined with a 'period' in conf.yaml
    Asynchronous Writers are defined WITHOUT a 'period' in conf.yaml.

    For synchronous Writers, write() is called after each 'period'.
    For asynchronous Writers, write() is called as soon a message arrives in
    the queue.
"""

import os
import time
import pickle
import logging
import threading

import helpers.kronos as kronos
from __exceptions import ConfigurationError

class Writer(threading.Thread):
    def __init__(self,
                 queue,                   # source of messages to be written
                 conf={},                 # additional configurations
                 blockable=True,          # stops if a message was not delivered
                 interval=None,           # interval to read from queue
                 retry_interval=1,        # retry interval (in seconds) to writing
                 retry_timeout=None,      # if timeout is reached, discard message
                 checkpoint_enabled=False,# deafult is to not deal with checkpoints
                 checkpoint_interval=60,  # interval of checkpoint writing
                 health_check_period=300  # period to log status
                 ):
        self.log = logging.getLogger()
        self.conf = conf
        self.processed = 0
        self.discarded = 0
        self.queue = queue
        self.blocked = False
        self.interval = interval
        self.blockable = blockable
        self.retry_timeout = retry_timeout
        self.retry_interval = retry_interval
        self.checkpoint_enabled = checkpoint_enabled
        self.health_check_period = health_check_period
        self.set_conf(conf)

        if self.checkpoint_enabled:
            self.last_checkpoint = self._read_checkpoint()
            if not hasattr(self, 'checkpoint_interval'):
                self.checkpoint_interval = checkpoint_interval
            if not hasattr(self, 'checkpoint_path'):
                self.log.error('Error. Please, configure a checkpoint_path')
                self.log.info('Aborting.')
                exit(-1)

        self.setup()

        if hasattr(self, 'required_confs'):
            self.validate_conf()

        self.scheduler = kronos.ThreadedScheduler()
        self.schedule_tasks()
        if self.checkpoint_enabled:
            self.schedule_checkpoint_writing()

        threading.Thread.__init__(self)

    def validate_conf(self):
        """Validate if required confs are present.
           required_confs are supposed to be set in setup() method.
        """
        for item in self.required_confs:
            if not hasattr(self, item):
                self.log.error('%s not defined in conf.yaml' % item)
                self.log.info('Aborting')
                raise ConfigurationError()

    def schedule_checkpoint_writing(self):
        self.scheduler.add_interval_task(self._write_checkpoint,
                                         "checkpoint writing",
                                         0,
                                         self.checkpoint_interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def _read_checkpoint(self):
        """Read checkpoint file from disk."""
        try:
            if not os.path.exists(self.checkpoint_path):
                self.log.info('No checkpoint found in %s.' % self.checkpoint_path)
                return ''
            f = open(self.checkpoint_path, 'rb')
            read = pickle.load(f)
            f.close()
            if read:
                return read
            else:
                return ''
            self.log.info("Checkpoint read from %s" % self.checkpoint_path)
        except Exception, e:
            self.log.error('Error reading checkpoint in %s' % self.checkpoint_path)
            self.log.error(e)

    def _write_checkpoint(self):
        """Write checkpoint in disk."""
        try:
            lc = self.last_checkpoint
            f = open(self.checkpoint_path, 'w')
            pickle.dump(lc, f)
            f.close()
            self.log.info('Checkpoint written: %s' % lc)
        except Exception, e:
            self.log.error('Error writing checkpoint in %s' % self.checkpoint_path)
            self.log.error(e)

    def set_conf(self, conf):
        """Turns configuration properties into instance properties."""
        try:
            for item in conf:
                if isinstance(conf[item], str):
                    exec("self.%s = '%s'" % (item, conf[item]))
                else:
                    exec("self.%s = %s" % (item, conf[item]))
            self.log.info("Configuration settings added with success into writer.")
        except Exception, e:
            self.log.error("Invalid configuration item: %s" % item)
            self.log.error(e)

    def reschedule_tasks(self):
        try:
            self.scheduler = kronos.ThreadedScheduler()
            self.schedule_tasks()
            if self.checkpoint_enabled:
                self.schedule_checkpoint_writing()
            self.scheduler.start()
            self.log.info("Success in rescheduling")
        except Exception, e:
            self.log.error("Error while rescheduling tasks")
            self.log.error(e)

    def schedule_tasks(self):
        try:
            if self.interval:
                self.schedule_interval_task()
            else:
                self.schedule_single_task()
        except Exception, e:
            self.log.error("Error while scheduling tasks")
            self.log.error(e)

    def schedule_single_task(self):
        self.scheduler.add_single_task(self.async_process,
                                       "single task",
                                       0,
                                       kronos.method.threaded,
                                       [],
                                       None)

    def schedule_interval_task(self):
        self.scheduler.add_interval_task(self.process,
                                         "periodic task",
                                         0,
                                         self.interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def retry_writing(self, msg):
        """Blocks writer till a message is written or a timeout is reached"""
        wrote = False
        time_passed = 0
        while True:
            self.log.info("Trying to rewrite message...")
            if self._write(msg.content):
                wrote = True
                self.processed += 1
                self.log.debug("Message written: %s" % msg)
                self.log.info("Rewriting done with success.")
                break
            elif self.retry_timeout and \
                 time_passed > self.retry_timeout:
                 self.discarded += 1
                 self.log.debug("Message: %s discarded due to timeout" % msg)
                 self.log.info("Retry timeout reached. Discarding message...")
                 break
            time.sleep(self.retry_interval)
            time_passed += self.retry_interval
        return wrote

    def async_process(self):
        """Method that processes (write) a message.
           It waits for new messages from the queue,
           and as soon as one arrives, it tries to deliver it.
        """
        while True:
            try:
                #blocks if none
                msg = self.queue.get()
                wrote = False
                if not self._write(msg.content):
                    if self.blockable:
                        self.blocked = True
                        self.log.warning("Writer blocked.")
                        wrote = self.retry_writing(msg)
                        self.blocked = False
                        self.log.info("Writer unblocked.")
                    else:
                        self.discarded += 1
                        self.log.info("Since it's not blockable, discarding message: %s" % msg)
                else:
                    wrote = True
                    self.processed += 1
                    self.log.debug("Message written: %s" % msg)

                if wrote and self.checkpoint_enabled:
                    self._set_checkpoint(msg.checkpoint)

            except Exception, e:
                self.log.error("Couldn't process message")
                self.log.error(e)

    def process(self):
        """Method called to process (write) a message.
            It is called in the end of each interval
            in the case of a periodic task.
            If it's an async writer it is called by a Reader as
            a callback.  So, it may be called by subclasses.
        """
        try:
            if self.queue.qsize() > 0:
                msg = self.queue.get()
                wrote = False
                if not self._write(msg.content):
                    if self.blockable:
                        self.scheduler.stop()
                        self.blocked = True
                        self.log.warning("Writer blocked.")

                        time_passed = 0
                        while True:
                            self.log.info("Trying to rewrite message...")
                            if self._write(msg.content):
                                wrote = True
                                self.log.debug("Message written: %s" % msg)
                                self.log.info("Rewriting done with success.")
                                break
                            elif self.retry_timeout and \
                                 time_passed > self.retry_timeout:
                                self.discarded += 1
                                self.log.info("Retry timeout reached. Discarding message...")
                                break
                            time.sleep(self.retry_interval)
                            time_passed += self.retry_interval
                        self.blocked = False
                        self.log.info("Writer unblocked.")
                        self.reschedule_tasks()
                    else:
                        self.discarded += 1
                        self.log.info("Since it's not blockable, discarding message: %s" % msg)
                else:
                    wrote = True
                    self.log.debug("Message written: %s" % msg)

                if wrote:
                    self.processed += 1
                    if self.checkpoint_enabled:
                        self._set_checkpoint(msg.checkpoint)
            else:
                self.log.debug("No messages in the queue to write.")
        except Exception, e:
            self.log.error("Couldn't process message")
            self.log.error(e)

    def _write(self, msg):
        """Method that calls write method defined by subclasses.
        Shouldn't be called by subclasses."""
        try:
            return self.write(msg)
        except Exception, e:
            self.log.error("Can't write message: %s" % msg)
            self.log.error(e)
            return False

    def setup(self):
        """Subclasses should implement."""

    def write(self, msg):
        """Subclasses should implement."""

    def _set_checkpoint(self, checkpoint):
        try:
            self.last_checkpoint = checkpoint
            self.log.debug("Last checkpoint: %s" %checkpoint)
        except Exception, e:
            self.log.error('Error updating last_checkpoint')
            self.log.error(e)

    def run(self):
        """Starts the writer"""
        self.scheduler.start()
        while True:
            self.log.debug("running")
            time.sleep(self.health_check_period)

    def __str__(self):
        return str(self.__dict__)
<|endoftext|>


<commit_before>
import time
import threading

import helpers.kronos as kronos

class Writer(threading.Thread):
    def __init__(self, queue, conf={}, blockable=False, interval=None):
        """queue: source of messages to write somewhere
           conf: additional configurations
           blockable: stops if a message were not delivered
           interval: interval to read from queue"""

        self.queue = queue
        self.interval = interval
        self.blockable = blockable
        self.processed = 0
        if conf:
            self.set_conf(conf)
        self.setup()
        self.schedule_tasks()
        threading.Thread.__init__(self)

    def set_conf(self, conf):
        """Turns configuration properties
            into instance properties."""
        try:
            for item in conf:
                if isinstance(conf[item], str):
                    exec("self.%s = '%s'" % (item, conf[item]))
                else:
                    exec("self.%s = %s" % (item, conf[item]))
        except Exception, e:
            print "Invalid configuration item: %s" % item
            print e

    def reschedule_tasks(self):
        self.schedule_tasks()
        self.scheduler.start()

    def schedule_tasks(self):
        self.scheduler = kronos.ThreadedScheduler()
        if self.interval:
            self.schedule_interval_task()

    def schedule_interval_task(self):
        self.scheduler.add_interval_task(self.process,
                                         "periodic task",
                                         0,
                                         self.interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def process(self):
        """Method called to process (write) a message.
            It is called in the end of each interval
            in the case of a periodic task.
            It it's a async writer it is called by a Reader as
            a callback.
            So, it may be called by subclasses."""

        if self.queue.qsize() > 0:
            msg = self.queue.get()
            if not self._write(msg):
                if self.blockable:
                    self.scheduler.stop()
                    while not self._write(msg):
                        print "Rewriting..."
                    self.reschedule_tasks()
                else:
                    print "Message [%s] can't be sent" % msg
            else:
                self.processed += 1
        else:
            print "No messages in the queue"

    def setup(self):
        """Subclasses should implement."""
        pass

    def _write(self, msg):
        """Method that calls write method defined by subclasses.
        Shouldn't be called by subclasses."""
        try:
            return self.write(msg)
        except Exception, e:
            print "Can't write"
            print e
            return False

    def write(self, msg):
        """Subclasses should implement."""
        pass

    def run(self):
        """Starts the writer"""
        self.scheduler.start()

<commit_msg>printing when message is written<commit_after>
import time
import threading

import helpers.kronos as kronos

class Writer(threading.Thread):
    def __init__(self, queue, conf={}, blockable=False, interval=None):
        """queue: source of messages to write somewhere
           conf: additional configurations
           blockable: stops if a message were not delivered
           interval: interval to read from queue"""

        self.queue = queue
        self.interval = interval
        self.blockable = blockable
        self.processed = 0
        if conf:
            self.set_conf(conf)
        self.setup()
        self.schedule_tasks()
        threading.Thread.__init__(self)

    def set_conf(self, conf):
        """Turns configuration properties
            into instance properties."""
        try:
            for item in conf:
                if isinstance(conf[item], str):
                    exec("self.%s = '%s'" % (item, conf[item]))
                else:
                    exec("self.%s = %s" % (item, conf[item]))
        except Exception, e:
            print "Invalid configuration item: %s" % item
            print e

    def reschedule_tasks(self):
        self.schedule_tasks()
        self.scheduler.start()

    def schedule_tasks(self):
        self.scheduler = kronos.ThreadedScheduler()
        if self.interval:
            self.schedule_interval_task()

    def schedule_interval_task(self):
        self.scheduler.add_interval_task(self.process,
                                         "periodic task",
                                         0,
                                         self.interval,
                                         kronos.method.threaded,
                                         [],
                                         None)

    def process(self):
        """Method called to process (write) a message.
            It is called in the end of each interval
            in the case of a periodic task.
            It it's a async writer it is called by a Reader as
            a callback.
            So, it may be called by subclasses."""

        if self.queue.qsize() > 0:
            msg = self.queue.get()
            if not self._write(msg):
                if self.blockable:
                    self.scheduler.stop()
                    while not self._write(msg):
                        print "Rewriting..."
                    self.reschedule_tasks()
                else:
                    print "Message [%s] can't be sent" % msg
            else:
                print "Message %s written" % msg
                self.processed += 1
        else:
            print "No messages in the queue"

    def setup(self):
        """Subclasses should implement."""
        pass

    def _write(self, msg):
        """Method that calls write method defined by subclasses.
        Shouldn't be called by subclasses."""
        try:
            return self.write(msg)
        except Exception, e:
            print "Can't write"
            print e
            return False

    def write(self, msg):
        """Subclasses should implement."""
        pass

    def run(self):
        """Starts the writer"""
        self.scheduler.start()

<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import package
import versions
import files

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    if pkg and pkg[0] != ":":
	pkg = cfg.packagenamespace + ":" + pkg

    for pkgName in repos.getPackageList(pkg):
	if versionStr or ls:
            _displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
            continue
	else:
	    if all:
		l = repos.getPackageVersionList(pkgName)
	    else:
		l = _versionList(repos, pkgName)

	    for version in l:
		print _pkgFormat % (
		    package.stripNamespace(cfg.packagenamespace, pkgName),
		    version.asString(cfg.defaultbranch))

def _versionList(repos, pkgName):
    """
    Returns a list of the head of all non-empty branches for a pakage.

    @param repos: Repository to look for branches in
    @type repos: repository.Repository
    @param pkgName: Name of a package
    @type pkgName: str
    @rtype: list of str
    """

    branches = repos.getPackageBranchList(pkgName)
    l = []
    for branch in branches:
	if not branch.isLocal():
	    version = repos.pkgLatestVersion(pkgName, branch)
	    # filter out empty branches
	    if version.onBranch(branch):
		l.append(version)

    return l

def _displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr[0] != "/":
	versionStr = cfg.defaultbranch.asString() + "/" + versionStr
    version = versions.VersionFromString(versionStr)

    if version.isBranch():
	pkg = repos.getLatestPackage(pkgName, version)
    else:
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version, path = path)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<commit_msg>do a (somewhat) better job of displaying groups<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import files
import log
import package
import versions

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"
_grpFormat  = "%-39s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    if pkg and pkg[0] != ":":
	pkg = cfg.packagenamespace + ":" + pkg

    if pkg and pkg.split(":")[2].startswith("group-"):
	nameList = pkg.split(":")
	nameList[2] = nameList[2][6:]
	name = ":".join(nameList)
	version = repos.grpLatestVersion(name, cfg.defaultbranch)
	grp = repos.getGroupVersion(name, version)

	for (pkg, verList) in grp.getPackageList():
	    for ver in verList:
		print _grpFormat % (
			    package.stripNamespace(cfg.packagenamespace, pkg),
			    ver.asString())
    else:
	list = repos.getPackageList(pkg)
	if not list:
	    log.warning("object %s does not exist" % pkg)
	    return

	for pkgName in list:
	    if versionStr or ls:
		_displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
		continue
	    else:
		if all:
		    l = repos.getPackageVersionList(pkgName)
		else:
		    l = _versionList(repos, pkgName)

		for version in l:
		    print _pkgFormat % (
			package.stripNamespace(cfg.packagenamespace, pkgName),
			version.asString(cfg.defaultbranch))

def _versionList(repos, pkgName):
    """
    Returns a list of the head of all non-empty branches for a package.

    @param repos: Repository to look for branches in
    @type repos: repository.Repository
    @param pkgName: Name of a package
    @type pkgName: str
    @rtype: list of str
    """

    branches = repos.getPackageBranchList(pkgName)
    l = []
    for branch in branches:
	if not branch.isLocal():
	    version = repos.pkgLatestVersion(pkgName, branch)
	    # filter out empty branches
	    if version.onBranch(branch):
		l.append(version)

    return l

def _displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr[0] != "/":
	versionStr = cfg.defaultbranch.asString() + "/" + versionStr
    version = versions.VersionFromString(versionStr)

    if version.isBranch():
	pkg = repos.getLatestPackage(pkgName, version)
    else:
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version, path = path)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import package
import versions
import files

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    if pkg and pkg[0] != ":":
	pkg = cfg.packagenamespace + ":" + pkg

    for pkgName in repos.getPackageList(pkg):
	if versionStr or ls:
            displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
            continue
	else:
	    if all:
		l = repos.getPackageVersionList(pkgName)
		versions.versionSort(l)
	    else:
                version = repos.pkgLatestVersion(pkgName, cfg.defaultbranch)
                if version:
                    l = ( version, )
                else:
                    l = ()

	    for version in l:
		print _pkgFormat % (
		    package.stripNamespace(cfg.packagenamespace, pkgName),
		    version.asString(cfg.defaultbranch))

def displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr:
	if versionStr[0] != "/":
	    versionStr = cfg.defaultbranch.asString() + "/" + versionStr
	version = versions.VersionFromString(versionStr)

	if version.isBranch():
	    pkg = repos.getLatestPackage(pkgName, version)
	else:
	    pkg = repos.getPackageVersion(pkgName, version)
    else:
	version = repos.pkgLatestVersion(pkgName, cfg.defaultbranch)
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<commit_msg>versionSort() no longer exists<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import package
import versions
import files

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    if pkg and pkg[0] != ":":
	pkg = cfg.packagenamespace + ":" + pkg

    for pkgName in repos.getPackageList(pkg):
	if versionStr or ls:
            displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
            continue
	else:
	    if all:
		l = repos.getPackageVersionList(pkgName)
	    else:
                version = repos.pkgLatestVersion(pkgName, cfg.defaultbranch)
                if version:
                    l = ( version, )
                else:
                    l = ()

	    for version in l:
		print _pkgFormat % (
		    package.stripNamespace(cfg.packagenamespace, pkgName),
		    version.asString(cfg.defaultbranch))

def displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr:
	if versionStr[0] != "/":
	    versionStr = cfg.defaultbranch.asString() + "/" + versionStr
	version = versions.VersionFromString(versionStr)

	if version.isBranch():
	    pkg = repos.getLatestPackage(pkgName, version)
	else:
	    pkg = repos.getPackageVersion(pkgName, version)
    else:
	version = repos.pkgLatestVersion(pkgName, cfg.defaultbranch)
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<|endoftext|>


<commit_before>import Tkinter as tk
import solver
import time
import sys

def _create_circle(self, x, y, r, **kwargs):
    return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle

def _coords_circle(self, target, x, y, r, **kwargs):
    return self.coords(target, x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.coords_circle = _coords_circle

def draw(ball_list,canvas):
    draw_list = []
    for ball in ball_list:
        position = ball.position.tolist()
        draw_list.append(canvas.create_circle(position[0], position[1], ball.radius, fill="white"))
    return draw_list

def move(ball_list,draw_list,canvas):
    for i in range(len(ball_list)):
        position = ball_list[i].position.tolist()
        canvas.coords_circle(draw_list[i],position[0], position[1],ball_list[i].radius)

def stop(event):
    canvas.destroy()

def animation(ball_list, draw_list, canvas, step, wlength):
    flag = True
    i = 0
    while(flag):
        solver.solve_step(ball_list,step,wlength)
        move(ball_list,draw_list,canvas)
        canvas.update()
        #time.sleep(step)
        #canvas.bind('<KeyPress>', stop)
        i+=1
        print i

def run(ball_list, step, wlength):
    window = tk.Tk()
    canvas = tk.Canvas(window, width=wlength, height=wlength, bg="black")
    canvas.pack()
    draw_list = draw(ball_list,canvas)
    canvas.pack()
    canvas.focus_set()
    window.after(0,animation(ball_list, draw_list, canvas, step, wlength))
    window.mainloop()<commit_msg>Remove useless stuff<commit_after>import Tkinter as tk
import solver
import time
import sys

def _create_circle(self, x, y, r, **kwargs):
    return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle

def _coords_circle(self, target, x, y, r, **kwargs):
    return self.coords(target, x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.coords_circle = _coords_circle

def draw(ball_list,canvas):
    draw_list = []
    for ball in ball_list:
        position = ball.position.tolist()
        draw_list.append(canvas.create_circle(position[0], position[1], ball.radius, fill="white"))
    return draw_list

def move(ball_list,draw_list,canvas):
    for i in range(len(ball_list)):
        position = ball_list[i].position.tolist()
        canvas.coords_circle(draw_list[i],position[0], position[1],ball_list[i].radius)

def stop(event):
    canvas.destroy()

def animation(ball_list, draw_list, canvas, step, wlength):
    flag = True
    while(flag):
        solver.solve_step(ball_list,step,wlength)
        move(ball_list,draw_list,canvas)
        canvas.update()
        #time.sleep(step)
        #canvas.bind('<KeyPress>', stop)

def run(ball_list, step, wlength):
    window = tk.Tk()
    canvas = tk.Canvas(window, width=wlength, height=wlength, bg="black")
    canvas.pack()
    draw_list = draw(ball_list,canvas)
    canvas.pack()
    canvas.focus_set()
    window.after(0,animation(ball_list, draw_list, canvas, step, wlength))
    window.mainloop()
<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import files
import log
import package
import packagename
import versions

from packagename import PackageName

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"
_grpFormat  = "%-39s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    if pkg and pkg[0] != ":":
	pkg = PackageName(cfg.packagenamespace + ":" + pkg)
    else:
	pkg = PackageName(pkg)

    if pkg.isGroup():
	if not repos.hasGroup(pkg):
	    log.error("group %s can not be found" %
			pkg.getName())
			#pkg.getName(cfg.packagenamespace))
	    return

	version = repos.grpLatestVersion(pkg, cfg.defaultbranch)
	grp = repos.getGroupVersion(pkg, version)

	for (pkg, verList) in grp.getPackageList():
	    for ver in verList:
		print _grpFormat % (
			    package.stripNamespace(cfg.packagenamespace, pkg),
			    ver.asString())
    else:
	list = repos.getPackageList(pkg)
	if not list:
	    log.warning("object %s does not exist" % pkg)
	    return

	for pkgName in list:
	    if versionStr or ls:
		_displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
		continue
	    else:
		if all:
		    l = repos.getPackageVersionList(pkgName)
		else:
		    l = _versionList(repos, pkgName)

		for version in l:
		    print _pkgFormat % (
			package.stripNamespace(cfg.packagenamespace, pkgName),
			version.asString(cfg.defaultbranch))

def _versionList(repos, pkgName):
    """
    Returns a list of the head of all non-empty branches for a package.

    @param repos: Repository to look for branches in
    @type repos: repository.Repository
    @param pkgName: Name of a package
    @type pkgName: str
    @rtype: list of str
    """

    branches = repos.getPackageBranchList(pkgName)
    l = []
    for branch in branches:
	if not branch.isLocal():
	    version = repos.pkgLatestVersion(pkgName, branch)
	    # filter out empty branches
	    if version.onBranch(branch):
		l.append(version)

    return l

def _displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr[0] != "/":
	versionStr = cfg.defaultbranch.asString() + "/" + versionStr
    version = versions.VersionFromString(versionStr)

    if version.isBranch():
	pkg = repos.getLatestPackage(pkgName, version)
    else:
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version, path = path)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<commit_msg>removed need for packagename.py for now to avoid having to dig out laptop<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#
import files
import log
import package
#import packagename
import versions

from packagename import PackageName

_pkgFormat  = "%-39s %s"
_fileFormat = "    %-35s %s"
_grpFormat  = "%-39s %s"

def displayPkgs(repos, cfg, all = 0, ls = 0, pkg = "", versionStr = None):
    #if pkg and pkg[0] != ":":
	#pkg = PackageName(cfg.packagenamespace + ":" + pkg)
    #else:
	#pkg = PackageName(pkg)

    #if pkg.isGroup():
    if 0:
	if not repos.hasGroup(pkg):
	    log.error("group %s can not be found" %
			pkg.getName())
			#pkg.getName(cfg.packagenamespace))
	    return

	version = repos.grpLatestVersion(pkg, cfg.defaultbranch)
	grp = repos.getGroupVersion(pkg, version)

	for (pkg, verList) in grp.getPackageList():
	    for ver in verList:
		print _grpFormat % (
			    package.stripNamespace(cfg.packagenamespace, pkg),
			    ver.asString())
    else:
	list = repos.getPackageList(pkg)
	if not list:
	    log.warning("object %s does not exist" % pkg)
	    return

	for pkgName in list:
	    if versionStr or ls:
		_displayPkgInfo(repos, cfg, pkgName, versionStr, ls)
		continue
	    else:
		if all:
		    l = repos.getPackageVersionList(pkgName)
		else:
		    l = _versionList(repos, pkgName)

		for version in l:
		    print _pkgFormat % (
			package.stripNamespace(cfg.packagenamespace, pkgName),
			version.asString(cfg.defaultbranch))

def _versionList(repos, pkgName):
    """
    Returns a list of the head of all non-empty branches for a package.

    @param repos: Repository to look for branches in
    @type repos: repository.Repository
    @param pkgName: Name of a package
    @type pkgName: str
    @rtype: list of str
    """

    branches = repos.getPackageBranchList(pkgName)
    l = []
    for branch in branches:
	if not branch.isLocal():
	    version = repos.pkgLatestVersion(pkgName, branch)
	    # filter out empty branches
	    if version.onBranch(branch):
		l.append(version)

    return l

def _displayPkgInfo(repos, cfg, pkgName, versionStr, ls):
    if versionStr[0] != "/":
	versionStr = cfg.defaultbranch.asString() + "/" + versionStr
    version = versions.VersionFromString(versionStr)

    if version.isBranch():
	pkg = repos.getLatestPackage(pkgName, version)
    else:
	pkg = repos.getPackageVersion(pkgName, version)

    if not ls:
	print _pkgFormat % (
	    package.stripNamespace(cfg.packagenamespace, pkgName),
	    version.asString(cfg.defaultbranch))

	for (fileId, path, version) in pkg.fileList():
	    print _fileFormat % (path, version.asString(cfg.defaultbranch))
    else:
	for (fileId, path, version) in pkg.fileList():
	    file = repos.getFileVersion(fileId, version, path = path)

	    if isinstance(file, files.SymbolicLink):
		name = "%s -> %s" %(path, file.linkTarget())
	    else:
		name = path

	    print "%s    1 %-8s %-8s %s %s %s" % \
		(file.modeString(), file.owner(), file.group(),
		 file.sizeString(), file.timeString(), name)
<|endoftext|>


<commit_before>from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from TWLight.users.models import Editor, UserProfile

def deactivate(modeladmin, request, queryset):
    for user in queryset:
        if hasattr(user, 'editor'):
            # Delete all optional info on Editor - except homewiki, which we'll
            # keep in order to be able to log them in should they ever
            # reactivate.
            # We can't delete the editor isntance entirely because that would
            # delete associated Applications, which we wish to preserve.
            user.editor.contributions = ''
            user.editor.real_name = ''
            user.editor.country_of_residence = ''
            user.editor.occupation = ''
            user.editor.affiliation = ''
            user.editor.save()

        user.userprofile.terms_of_use = False
        user.userprofile.save()

        user.email = ''
        user.is_active = False
        user.save()

deactivate.short_description = \
    "Deactivate selected accounts"

class EditorInline(admin.StackedInline):
    model = Editor
    max_num = 1
    extra = 1
    can_delete = False
    raw_id_fields = ("user",)

class UserProfileInline(admin.StackedInline):
    model = UserProfile
    max_num = 1
    extra = 1
    can_delete = False
    raw_id_fields = ("user",)

class UserAdmin(AuthUserAdmin):
    inlines = [EditorInline, UserProfileInline]
    actions = [deactivate]
    list_display = ['username', 'get_wp_username', 'get_wiki', 'is_staff']
    list_filter = ['is_staff', 'is_active', 'is_superuser']
    default_filters = ['is_active__exact=1']

    def get_wp_username(self, user):
        if hasattr(user, 'editor'):
            return user.editor.wp_username
        else:
            return ''
    get_wp_username.short_description = _('Wikipedia username')

    def get_wiki(self, user):
        if hasattr(user, 'editor'):
            return user.editor.home_wiki
        else:
            return ''
    get_wiki.short_description = _('Home wiki')

# Unregister old user admin; register new, improved user admin.
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
<commit_msg>Improve admin usability as user base gets large<commit_after>from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from TWLight.users.models import Editor, UserProfile

def deactivate(modeladmin, request, queryset):
    for user in queryset:
        if hasattr(user, 'editor'):
            # Delete all optional info on Editor - except homewiki, which we'll
            # keep in order to be able to log them in should they ever
            # reactivate.
            # We can't delete the editor isntance entirely because that would
            # delete associated Applications, which we wish to preserve.
            user.editor.contributions = ''
            user.editor.real_name = ''
            user.editor.country_of_residence = ''
            user.editor.occupation = ''
            user.editor.affiliation = ''
            user.editor.save()

        user.userprofile.terms_of_use = False
        user.userprofile.save()

        user.email = ''
        user.is_active = False
        user.save()

deactivate.short_description = \
    "Deactivate selected accounts"

class EditorInline(admin.StackedInline):
    model = Editor
    max_num = 1
    extra = 1
    can_delete = False
    raw_id_fields = ("user",)

class UserProfileInline(admin.StackedInline):
    model = UserProfile
    max_num = 1
    extra = 1
    can_delete = False
    raw_id_fields = ("user",)

class UserAdmin(AuthUserAdmin):
    inlines = [EditorInline, UserProfileInline]
    actions = [deactivate]
    list_display = ['username', 'get_wp_username', 'get_wiki', 'is_staff']
    list_filter = ['is_staff', 'is_active', 'is_superuser']
    default_filters = ['is_active__exact=1']
    search_fields = ['editor__wp_username', 'username']

    def get_wp_username(self, user):
        if hasattr(user, 'editor'):
            return user.editor.wp_username
        else:
            return ''
    get_wp_username.short_description = _('Wikipedia username')

    def get_wiki(self, user):
        if hasattr(user, 'editor'):
            return user.editor.home_wiki
        else:
            return ''
    get_wiki.short_description = _('Home wiki')

# Unregister old user admin; register new, improved user admin.
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
<|endoftext|>


<commit_before>from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib import messages
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.models import User
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse_lazy, resolve, Resolver404
from django.http import Http404
from django.views.generic.base import TemplateView, View
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView
from django.utils.decorators import classonlymethod
from django.utils.http import is_safe_url
from django.utils.translation import ugettext_lazy as _

from TWLight.view_mixins import CoordinatorsOrSelf, SelfOnly, coordinators

from .forms import EditorUpdateForm, SetLanguageForm, TermsForm
from .models import Editor, UserProfile

class UserDetailView(SelfOnly, TemplateView):
    template_name = 'users/user_detail.html'

    def get_object(self):
        """
        Although 'user' is part of the default context, we need to define a
        get_object in order to be able to use the SelfOnly mixin.
        """
        assert 'pk' in self.kwargs.keys()
        try:
            return User.objects.get(pk=self.kwargs['pk'])
        except User.DoesNotExist:
            raise Http404

    def get_context_data(self, **kwargs):
        context = super(UserDetailView, self).get_context_data(**kwargs)
        context['language_form'] = SetLanguageForm()
        context['password_form'] = PasswordChangeForm(user=self.request.user)
        return context

class EditorDetailView(CoordinatorsOrSelf, DetailView):
    """
    User profile data page for users who are Editors. Uses the Editor model,
    because:
    1) That's where most of the data is;
    2) Using the Editor model means its URL parameter is consistent with that of
       EditorUpdateView, because you know Wikipedians will employ URL hacking
       to get places on the site, and this simplifies that.
    """
    model = Editor
    template_name = 'users/editor_detail.html'

    def get_context_data(self, **kwargs):
        context = super(EditorDetailView, self).get_context_data(**kwargs)
        editor = self.get_object()
        context['editor'] = editor # allow for more semantic templates
        context['object_list'] = editor.applications.all().order_by('status')
        context['form'] = EditorUpdateForm(instance=editor)
        context['language_form'] = SetLanguageForm()

        if self.request.user.editor == editor and not editor.contributions:
            messages.add_message(self.request, messages.WARNING,
                _('Please update your contributions to Wikipedia (below) to '
                  'help coordinators evaluate your applications.'))

        context['password_form'] = PasswordChangeForm(user=self.request.user)

        return context

class UserHomeView(View):
    """
    The view for users to see their own profile data.

    Dispatches to either EditorDetailView, if the User has an attached Editor,
    or UserDetailView otherwise.

    Does not do any access limitation; EditorDetailView and UserDetailView
    are responsible for that.
    """
    editor_view = EditorDetailView
    non_editor_view = UserDetailView

    @classonlymethod
    def as_view(cls):
        def _get_view(request, *args, **kwargs):
            if request.user.is_anonymous():
                # We can't use something like django-braces LoginRequiredMixin
                # here, because as_view applies at an earlier point in the
                # process.
                return redirect_to_login(request.get_full_path())
            if hasattr(request.user, 'editor'):
                kwargs.update({'pk': request.user.editor.pk})
                return_view = cls.editor_view
            else:
                kwargs.update({'pk': request.user.pk})
                return_view = cls.non_editor_view

            return return_view.as_view()(request, *args, **kwargs)

        return _get_view

class EditorUpdateView(SelfOnly, UpdateView):
    """
    Allow Editors to add the information we can't harvest from OAuth, but
    would nonetheless like to have.

    This is intended for user during the original OAuth user creation flow, but
    can also be used if we'd like to let editors change this info later.

    We handle personally identifying information through PIIUpdateView.
    """
    model = Editor
    template_name = 'users/editor_update.html'
    form_class = EditorUpdateForm

class PIIUpdateView(SelfOnly, UpdateView):
    """
    Allow Editors to add the information we can't harvest from OAuth, but
    would nonetheless like to have.

    This is intended for user during the original OAuth user creation flow, but
    can also be used if we'd like to let editors change this info later.

    We handle personally identifying information through PIIUpdateView.
    """
    model = Editor
    template_name = 'users/editor_update.html'
    fields = ['real_name', 'country_of_residence', 'occupation', 'affiliation']

    def get_object(self):
        """
        Users may only update their own information.
        """
        try:
            assert self.request.user.is_authenticated()
        except AssertionError:
            raise PermissionDenied

        return self.request.user.editor

    def get_form(self, form_class):
        """
        Define get_form so that we can apply crispy styling.
        """
        form = super(PIIUpdateView, self).get_form(form_class)
        form.helper = FormHelper()
        form.helper.add_input(Submit(
            'submit',
            _('Update information'),
            css_class='center-block'))

        return form

    def get_success_url(self):
        """
        Define get_success_url so that we can add a success message.
        """
        messages.add_message(self.request, messages.SUCCESS,
            _('Your information has been updated.'))
        return reverse_lazy('users:home')

class EmailChangeView(SelfOnly, UpdateView):
    model = User
    template_name = 'users/editor_update.html'
    fields = ['email']

    def get_object(self):
        """
        Users may only update their own email.
        """
        try:
            assert self.request.user.is_authenticated()
        except AssertionError:
            raise PermissionDenied

        return self.request.user

    def get_form(self, form_class):
        """
        Define get_form so that we can apply crispy styling.
        """
        form = super(EmailChangeView, self).get_form(form_class)
        form.helper = FormHelper()
        form.helper.add_input(Submit(
            'submit',
            _('Update email'),
            css_class='center-block'))

        return form

    def get_success_url(self):
        """
        Define get_success_url so that we can add a success message.
        """
        messages.add_message(self.request, messages.SUCCESS,
            _('Your email has been changed to {email}.').format(
                email=self.request.user.email))
        return reverse_lazy('users:home')

class TermsView(UpdateView):
    """
    For authenticated users, this is a perfectly normal UpdateView that updates
    their UserProfile with their decision on agreeing with the Terms of Use. It
    also displays those terms.

    For anonymous users, this view still displays the terms, but it does not
    generate or show the form for agreeing with the terms, because that wouldn't
    make any sense.
    """
    model = UserProfile
    template_name = 'users/terms.html'
    form_class = TermsForm

    def get_object(self, queryset=None):
        """
        For authenticated users, returns their associated UserProfile instance.
        For anonymous users, returns None.
        """
        if self.request.user.is_authenticated():
            return self.request.user.userprofile
        else:
            return None

    def get_form(self, form_class):
        """
        For authenticated users, returns an instance of the form to be used in
        this view. For anonymous users, returns None.
        """
        kwargs = self.get_form_kwargs()

        if 'instance' not in kwargs:
            return None
        else:
            return form_class(**self.get_form_kwargs())

    def get_form_kwargs(self):
        """
        For authenticated users, returns the keyword arguments for instantiating
        the form. For anonymous users, returns None.
        """
        kwargs = super(TermsView, self).get_form_kwargs()
        if self.request.user.is_authenticated():
            kwargs.update({'instance': self.request.user.userprofile})
        return kwargs

    def get_success_url(self):
        def is_real_url(url):
            """
            Users might have altered the URL parameters. Let's not just blindly
            redirect; let's actually make sure we can get somewhere first.
            """
            try:
                resolve(url)
                return True
            except Resolver404:
                return False

        if self.get_object().terms_of_use:
            # If they agreed with the terms, awesome. Send them where they were
            # trying to go, if there's a meaningful `next` parameter in the URL;
            # if not, send them to their home page as a sensible default.
            next_param = self.request.GET.get(REDIRECT_FIELD_NAME, '')
            if (next_param and
                is_safe_url(url=next_param, host=self.request.get_host()) and
                is_real_url(next_param)):
                return next_param
            else:
                return reverse_lazy('users:home')

        else:
            # If they didn't agree, that's cool, but we should make sure they
            # know about the limits. Send them to their home page rather than
            # trying to parse the next parameter, because parsing next will
            # put them in an obnoxious redirect loop - send them to where
            # they were going, which promptly sends them back to the terms
            # page because they haven't agreed to the terms....
            if self.request.user in coordinators.user_set.all():
                fail_msg = _('You may explore the site, but you will not be '
                  'able to apply for access to materials or evaluate '
                  'applications unless you agree with the terms of use.')
            else:
                fail_msg = _('You may explore the site, but you will not be '
                  'able to apply for access to materials unless you agree with '
                  'the terms of use.')

            messages.add_message(self.request, messages.WARNING, fail_msg)
            return reverse_lazy('users:home')
<commit_msg>Fix bug whereby some users can't see user pages<commit_after>from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib import messages
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.models import User
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse_lazy, resolve, Resolver404
from django.http import Http404
from django.views.generic.base import TemplateView, View
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView
from django.utils.decorators import classonlymethod
from django.utils.http import is_safe_url
from django.utils.translation import ugettext_lazy as _

from TWLight.view_mixins import CoordinatorsOrSelf, SelfOnly, coordinators

from .forms import EditorUpdateForm, SetLanguageForm, TermsForm
from .models import Editor, UserProfile

class UserDetailView(SelfOnly, TemplateView):
    template_name = 'users/user_detail.html'

    def get_object(self):
        """
        Although 'user' is part of the default context, we need to define a
        get_object in order to be able to use the SelfOnly mixin.
        """
        assert 'pk' in self.kwargs.keys()
        try:
            return User.objects.get(pk=self.kwargs['pk'])
        except User.DoesNotExist:
            raise Http404

    def get_context_data(self, **kwargs):
        context = super(UserDetailView, self).get_context_data(**kwargs)
        context['language_form'] = SetLanguageForm()
        context['password_form'] = PasswordChangeForm(user=self.request.user)
        return context

class EditorDetailView(CoordinatorsOrSelf, DetailView):
    """
    User profile data page for users who are Editors. Uses the Editor model,
    because:
    1) That's where most of the data is;
    2) Using the Editor model means its URL parameter is consistent with that of
       EditorUpdateView, because you know Wikipedians will employ URL hacking
       to get places on the site, and this simplifies that.
    """
    model = Editor
    template_name = 'users/editor_detail.html'

    def get_context_data(self, **kwargs):
        context = super(EditorDetailView, self).get_context_data(**kwargs)
        editor = self.get_object()
        context['editor'] = editor # allow for more semantic templates
        context['object_list'] = editor.applications.all().order_by('status')
        context['form'] = EditorUpdateForm(instance=editor)
        context['language_form'] = SetLanguageForm()

        try:
            if self.request.user.editor == editor and not editor.contributions:
                messages.add_message(self.request, messages.WARNING,
                    _('Please update your contributions to Wikipedia (below) to '
                      'help coordinators evaluate your applications.'))
        except Editor.DoesNotExist:
            """
            If the user viewing the site does not have an attached editor
            (which can happen for administrative users), this error will be
            thrown, preventing the user from viewing the site. We don't actually
            want to have a 500 in this case, though; we just want to not add the
            message, and move on.
            """
            pass

        context['password_form'] = PasswordChangeForm(user=self.request.user)

        return context

class UserHomeView(View):
    """
    The view for users to see their own profile data.

    Dispatches to either EditorDetailView, if the User has an attached Editor,
    or UserDetailView otherwise.

    Does not do any access limitation; EditorDetailView and UserDetailView
    are responsible for that.
    """
    editor_view = EditorDetailView
    non_editor_view = UserDetailView

    @classonlymethod
    def as_view(cls):
        def _get_view(request, *args, **kwargs):
            if request.user.is_anonymous():
                # We can't use something like django-braces LoginRequiredMixin
                # here, because as_view applies at an earlier point in the
                # process.
                return redirect_to_login(request.get_full_path())
            if hasattr(request.user, 'editor'):
                kwargs.update({'pk': request.user.editor.pk})
                return_view = cls.editor_view
            else:
                kwargs.update({'pk': request.user.pk})
                return_view = cls.non_editor_view

            return return_view.as_view()(request, *args, **kwargs)

        return _get_view

class EditorUpdateView(SelfOnly, UpdateView):
    """
    Allow Editors to add the information we can't harvest from OAuth, but
    would nonetheless like to have.

    This is intended for user during the original OAuth user creation flow, but
    can also be used if we'd like to let editors change this info later.

    We handle personally identifying information through PIIUpdateView.
    """
    model = Editor
    template_name = 'users/editor_update.html'
    form_class = EditorUpdateForm

class PIIUpdateView(SelfOnly, UpdateView):
    """
    Allow Editors to add the information we can't harvest from OAuth, but
    would nonetheless like to have.

    This is intended for user during the original OAuth user creation flow, but
    can also be used if we'd like to let editors change this info later.

    We handle personally identifying information through PIIUpdateView.
    """
    model = Editor
    template_name = 'users/editor_update.html'
    fields = ['real_name', 'country_of_residence', 'occupation', 'affiliation']

    def get_object(self):
        """
        Users may only update their own information.
        """
        try:
            assert self.request.user.is_authenticated()
        except AssertionError:
            raise PermissionDenied

        return self.request.user.editor

    def get_form(self, form_class):
        """
        Define get_form so that we can apply crispy styling.
        """
        form = super(PIIUpdateView, self).get_form(form_class)
        form.helper = FormHelper()
        form.helper.add_input(Submit(
            'submit',
            _('Update information'),
            css_class='center-block'))

        return form

    def get_success_url(self):
        """
        Define get_success_url so that we can add a success message.
        """
        messages.add_message(self.request, messages.SUCCESS,
            _('Your information has been updated.'))
        return reverse_lazy('users:home')

class EmailChangeView(SelfOnly, UpdateView):
    model = User
    template_name = 'users/editor_update.html'
    fields = ['email']

    def get_object(self):
        """
        Users may only update their own email.
        """
        try:
            assert self.request.user.is_authenticated()
        except AssertionError:
            raise PermissionDenied

        return self.request.user

    def get_form(self, form_class):
        """
        Define get_form so that we can apply crispy styling.
        """
        form = super(EmailChangeView, self).get_form(form_class)
        form.helper = FormHelper()
        form.helper.add_input(Submit(
            'submit',
            _('Update email'),
            css_class='center-block'))

        return form

    def get_success_url(self):
        """
        Define get_success_url so that we can add a success message.
        """
        messages.add_message(self.request, messages.SUCCESS,
            _('Your email has been changed to {email}.').format(
                email=self.request.user.email))
        return reverse_lazy('users:home')

class TermsView(UpdateView):
    """
    For authenticated users, this is a perfectly normal UpdateView that updates
    their UserProfile with their decision on agreeing with the Terms of Use. It
    also displays those terms.

    For anonymous users, this view still displays the terms, but it does not
    generate or show the form for agreeing with the terms, because that wouldn't
    make any sense.
    """
    model = UserProfile
    template_name = 'users/terms.html'
    form_class = TermsForm

    def get_object(self, queryset=None):
        """
        For authenticated users, returns their associated UserProfile instance.
        For anonymous users, returns None.
        """
        if self.request.user.is_authenticated():
            return self.request.user.userprofile
        else:
            return None

    def get_form(self, form_class):
        """
        For authenticated users, returns an instance of the form to be used in
        this view. For anonymous users, returns None.
        """
        kwargs = self.get_form_kwargs()

        if 'instance' not in kwargs:
            return None
        else:
            return form_class(**self.get_form_kwargs())

    def get_form_kwargs(self):
        """
        For authenticated users, returns the keyword arguments for instantiating
        the form. For anonymous users, returns None.
        """
        kwargs = super(TermsView, self).get_form_kwargs()
        if self.request.user.is_authenticated():
            kwargs.update({'instance': self.request.user.userprofile})
        return kwargs

    def get_success_url(self):
        def is_real_url(url):
            """
            Users might have altered the URL parameters. Let's not just blindly
            redirect; let's actually make sure we can get somewhere first.
            """
            try:
                resolve(url)
                return True
            except Resolver404:
                return False

        if self.get_object().terms_of_use:
            # If they agreed with the terms, awesome. Send them where they were
            # trying to go, if there's a meaningful `next` parameter in the URL;
            # if not, send them to their home page as a sensible default.
            next_param = self.request.GET.get(REDIRECT_FIELD_NAME, '')
            if (next_param and
                is_safe_url(url=next_param, host=self.request.get_host()) and
                is_real_url(next_param)):
                return next_param
            else:
                return reverse_lazy('users:home')

        else:
            # If they didn't agree, that's cool, but we should make sure they
            # know about the limits. Send them to their home page rather than
            # trying to parse the next parameter, because parsing next will
            # put them in an obnoxious redirect loop - send them to where
            # they were going, which promptly sends them back to the terms
            # page because they haven't agreed to the terms....
            if self.request.user in coordinators.user_set.all():
                fail_msg = _('You may explore the site, but you will not be '
                  'able to apply for access to materials or evaluate '
                  'applications unless you agree with the terms of use.')
            else:
                fail_msg = _('You may explore the site, but you will not be '
                  'able to apply for access to materials unless you agree with '
                  'the terms of use.')

            messages.add_message(self.request, messages.WARNING, fail_msg)
            return reverse_lazy('users:home')
<|endoftext|>


<commit_before>import sys, math, pdb
from datetime import timedelta
import pandas as pd
import logging

logger = logging.getLogger('sopernovus')

SESSION_THRESHOLD_IN_MINUTES = 15

def summarize_applications(df, odf):
    """ Create a summary of the applications as a table. Presumes events are in datetime order
          * Count number of comments for different roles
    """

    prevApplicationId = None
    startIndex = 0
    i = 0

    summary = None

    df = df.sort_values(['applicationId', 'datetime'])

    numberOfApplications = df["applicationId"].nunique()

    logger.info("Analyzing events")

    applicationIds = df['applicationId'].unique()
    for applicationId in applicationIds:
        appInfo = odf[odf['applicationId'] == applicationId]
        if appInfo.empty:
            logger.error("No operative data available for application " + applicationId)
            continue

        app = parse_application_summary(applicationId, appInfo.iloc[0].to_dict(), df[df['applicationId'] == applicationId])

        if summary is None:
            summary = pd.DataFrame(app, index = [0])
        else:
            summary.loc[len(summary)] = app

    if odf is not None:
        summary = pd.merge(summary, odf, on = 'applicationId')

    return summary

def parse_application_summary(applicationId, appInfo, events):
    app = {    "applicationId": applicationId,
                "events": len(events),
				"userIds": find_unique_users_by_application(events),
                "comments": len(find_events_by_action_and_target(events, 'add-comment', 'application')),
                "commentsApplicant": len(find_events_by_action_and_role_and_target(events, 'add-comment', 'applicant', 'application')),
                "commentsAuthority": len(find_events_by_action_and_role_and_target(events, 'add-comment', 'authority', 'application')),
                "invitesAuthority" : len(find_events_by_action_and_role(events,'invite-with-role','authority')),
                "invitesApplicant": len(find_events_by_action_and_role(events,'invite-with-role','applicant')),
                "sessionLength": count_session_length(events, SESSION_THRESHOLD_IN_MINUTES),
                "sessionLengthApplicant": count_session_length_by_role(events, 'applicant', SESSION_THRESHOLD_IN_MINUTES),
                "sessionLengthAuthority": count_session_length_by_role(events, 'authority', SESSION_THRESHOLD_IN_MINUTES),
                "updateDocs": len(find_events_by_action(events, 'update-doc')),
                "createDocs": len(find_events_by_action(events, 'create-doc')),
                "isSubmitted": len(find_events_by_action(events, 'submit-application')) > 0,
                "hasVerdict": len(find_events_by_action(events, 'give-verdict')) > 0,
                "isCancelled": len(find_events_by_action(events, 'cancel-application')) > 0 or
                                len(find_events_by_action(events, 'cancel-application-authority')) > 0,
                "daysFromSubmissionToVerdict": count_days_between_events(events, 'submit-application', 'give-verdict'),
                "leadTimeCreated2Submitted": count_days(appInfo, 'createdDate', 'submittedDate'),
                "leadTimeSubmitted2Sent": count_days(appInfo, 'submittedDate', 'sentDate'),
                "leadTimeSent2VerdictGiven": count_days(appInfo, 'sentDate', 'verdictGivenDate'),
                "leadTimeCreated2Sent": count_days(appInfo, 'createdDate', 'sentDate'),
                "leadTime": count_days(appInfo, 'createdDate', 'verdictGivenDate'),
                "leadTimeCreated2Canceled": count_days(appInfo, 'createdDate', 'canceledDate'),
                "leadTimeSubmitted2VerdictGiven": count_days(appInfo, 'submittedDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Submitted": count_flow_efficiency(appInfo, events, 'createdDate', 'submittedDate'),
                "flowEfficiencySubmitted2Sent": count_flow_efficiency(appInfo, events, 'submittedDate', 'sentDate'),
                "flowEfficiencySent2VerdictGiven": count_flow_efficiency(appInfo, events, 'sentDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Sent": count_flow_efficiency(appInfo, events, 'createdDate', 'sentDate'),
                "flowEfficiency": count_flow_efficiency(appInfo, events, 'createdDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Canceled": count_flow_efficiency(appInfo, events, 'createdDate', 'canceledDate'),
                "flowEfficiencySubmitted2VerdictGiven": count_flow_efficiency(appInfo, events, 'submittedDate', 'verdictGivenDate')
            }

    return app

def find_unique_users_by_application(events):
	return events.userId.nunique()

def find_events_by_action(events, action):
    return events[events['action'] == action]

def find_events_by_action_and_target(events, action, target):
    result = events[events['action'] == action]
    result = events[events['target'] == target]
    return result

def find_events_by_action_and_role(events, action, role):
    # TODO: one liner would be better, but & is not supported
    result = events[events['action'] == action]
    result = result[result['role'] == role]
    return result

def find_events_by_action_and_role_and_target(events, action, role, target):
    # TODO: one liner would be better, but & is not supported
    result = find_events_by_action_and_role(events, action, role)
    result = result[result['role'] == role]
    result = result[result['target'] == target]
    return result

def show_progress_bar(index, maxIndex):
    if index % 1000 == 0:
        logger.info("Processed {}/{} rows".format(index, maxIndex))

def count_session_length(events, thresholdMinutes):
    delta = timedelta(minutes = thresholdMinutes)

    timestamps = events['datetime']

    if(len(timestamps) == 0):
        return 0

    prev = timestamps.iloc[0]
    i = 1
    totalSession = 0
    while i < len(timestamps):
        diff = timestamps.iloc[i] - prev
        prev = timestamps.iloc[i]
        if(diff < delta):
            totalSession = totalSession + diff.total_seconds()

        i = i + 1

    return round(totalSession / 60, 0)

def count_session_length_by_role(events, role, thresholdMinutes):
    return count_session_length(events[events['role'] == role], thresholdMinutes)

def count_days_between_events(events, fromEvent, tillEvent):
    e1 = find_events_by_action(events, fromEvent)
    e2 = find_events_by_action(events, tillEvent)

    if(len(e1) > 0 and len(e2) > 0):
        firstSubmission = e1.iloc(0)
        verdictGiven = e2.iloc(0)
        timeBetween = e2['datetime'].iloc[0] - e1['datetime'].iloc[0]
        return timeBetween.days + 1
    else:
        return None

def count_days(app, fromDate, tillDate):
    delta = app[tillDate] - app[fromDate]

    if pd.isnull(delta):
        return None
    else:
        return int(delta.days + 1)

def count_flow_efficiency(app, events, fromDate, tillDate):
    days = count_days(app, fromDate, tillDate)
    if days is None:
        return None

    nOfProcessedDays = len(events['datetime'].dt.normalize().unique())
    flowEfficiency = int(round(float(nOfProcessedDays) / days, 2) * 100)
    return flowEfficiency
<commit_msg>Negative lead time from timestamps ignored<commit_after>import sys, math, pdb
from datetime import timedelta
import pandas as pd
import logging

logger = logging.getLogger(__name__)

SESSION_THRESHOLD_IN_MINUTES = 15

def summarize_applications(df, odf):
    """ Create a summary of the applications as a table. Presumes events are in datetime order
          * Count number of comments for different roles
    """

    prevApplicationId = None
    startIndex = 0
    i = 0

    summary = None

    df = df.sort_values(['applicationId', 'datetime'])

    numberOfApplications = df["applicationId"].nunique()

    logger.info("Analyzing events")

    applicationIds = df['applicationId'].unique()
    for applicationId in applicationIds:
        appInfo = odf[odf['applicationId'] == applicationId]
        if appInfo.empty:
            logger.info("No operative data available for application (infoRequest) " + applicationId)
            continue

        app = parse_application_summary(applicationId, appInfo.iloc[0].to_dict(), df[df['applicationId'] == applicationId])

        if summary is None:
            summary = pd.DataFrame(app, index = [0])
        else:
            summary.loc[len(summary)] = app

    if odf is not None:
        summary = pd.merge(summary, odf, on = 'applicationId')

    return summary

def parse_application_summary(applicationId, appInfo, events):
    app = {    "applicationId": applicationId,
                "events": len(events),
				"userIds": find_unique_users_by_application(events),
                "comments": len(find_events_by_action_and_target(events, 'add-comment', 'application')),
                "commentsApplicant": len(find_events_by_action_and_role_and_target(events, 'add-comment', 'applicant', 'application')),
                "commentsAuthority": len(find_events_by_action_and_role_and_target(events, 'add-comment', 'authority', 'application')),
                "invitesAuthority" : len(find_events_by_action_and_role(events,'invite-with-role','authority')),
                "invitesApplicant": len(find_events_by_action_and_role(events,'invite-with-role','applicant')),
                "sessionLength": count_session_length(events, SESSION_THRESHOLD_IN_MINUTES),
                "sessionLengthApplicant": count_session_length_by_role(events, 'applicant', SESSION_THRESHOLD_IN_MINUTES),
                "sessionLengthAuthority": count_session_length_by_role(events, 'authority', SESSION_THRESHOLD_IN_MINUTES),
                "updateDocs": len(find_events_by_action(events, 'update-doc')),
                "createDocs": len(find_events_by_action(events, 'create-doc')),
                "isSubmitted": len(find_events_by_action(events, 'submit-application')) > 0,
                "hasVerdict": len(find_events_by_action(events, 'give-verdict')) > 0,
                "isCancelled": len(find_events_by_action(events, 'cancel-application')) > 0 or
                                len(find_events_by_action(events, 'cancel-application-authority')) > 0,
                "daysFromSubmissionToVerdict": count_days_between_events(events, 'submit-application', 'give-verdict'),
                "leadTimeCreated2Submitted": count_days(appInfo, 'createdDate', 'submittedDate'),
                "leadTimeSubmitted2Sent": count_days(appInfo, 'submittedDate', 'sentDate'),
                "leadTimeSent2VerdictGiven": count_days(appInfo, 'sentDate', 'verdictGivenDate'),
                "leadTimeCreated2Sent": count_days(appInfo, 'createdDate', 'sentDate'),
                "leadTime": count_days(appInfo, 'createdDate', 'verdictGivenDate'),
                "leadTimeCreated2Canceled": count_days(appInfo, 'createdDate', 'canceledDate'),
                "leadTimeSubmitted2VerdictGiven": count_days(appInfo, 'submittedDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Submitted": count_flow_efficiency(appInfo, events, 'createdDate', 'submittedDate'),
                "flowEfficiencySubmitted2Sent": count_flow_efficiency(appInfo, events, 'submittedDate', 'sentDate'),
                "flowEfficiencySent2VerdictGiven": count_flow_efficiency(appInfo, events, 'sentDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Sent": count_flow_efficiency(appInfo, events, 'createdDate', 'sentDate'),
                "flowEfficiency": count_flow_efficiency(appInfo, events, 'createdDate', 'verdictGivenDate'),
                "flowEfficiencyCreated2Canceled": count_flow_efficiency(appInfo, events, 'createdDate', 'canceledDate'),
                "flowEfficiencySubmitted2VerdictGiven": count_flow_efficiency(appInfo, events, 'submittedDate', 'verdictGivenDate')
            }

    return app

def find_unique_users_by_application(events):
	return events.userId.nunique()

def find_events_by_action(events, action):
    return events[events['action'] == action]

def find_events_by_action_and_target(events, action, target):
    result = events[events['action'] == action]
    result = events[events['target'] == target]
    return result

def find_events_by_action_and_role(events, action, role):
    # TODO: one liner would be better, but & is not supported
    result = events[events['action'] == action]
    result = result[result['role'] == role]
    return result

def find_events_by_action_and_role_and_target(events, action, role, target):
    # TODO: one liner would be better, but & is not supported
    result = find_events_by_action_and_role(events, action, role)
    result = result[result['role'] == role]
    result = result[result['target'] == target]
    return result

def show_progress_bar(index, maxIndex):
    if index % 1000 == 0:
        logger.info("Processed {}/{} rows".format(index, maxIndex))

def count_session_length(events, thresholdMinutes):
    delta = timedelta(minutes = thresholdMinutes)

    timestamps = events['datetime']

    if(len(timestamps) == 0):
        return 0

    prev = timestamps.iloc[0]
    i = 1
    totalSession = 0
    while i < len(timestamps):
        diff = timestamps.iloc[i] - prev
        prev = timestamps.iloc[i]
        if(diff < delta):
            totalSession = totalSession + diff.total_seconds()

        i = i + 1

    return round(totalSession / 60, 0)

def count_session_length_by_role(events, role, thresholdMinutes):
    return count_session_length(events[events['role'] == role], thresholdMinutes)

def count_days_between_events(events, fromEvent, tillEvent):
    e1 = find_events_by_action(events, fromEvent)
    e2 = find_events_by_action(events, tillEvent)

    if(len(e1) > 0 and len(e2) > 0):
        firstSubmission = e1.iloc(0)
        verdictGiven = e2.iloc(0)
        timeBetween = e2['datetime'].iloc[0] - e1['datetime'].iloc[0]
        return timeBetween.days + 1
    else:
        return None

def count_days(app, fromDate, tillDate):
    delta = app[tillDate] - app[fromDate]

    if pd.isnull(delta):
        return None
    else:
        return int(delta.days + 1)

def count_flow_efficiency(app, events, fromDate, tillDate):
    days = count_days(app, fromDate, tillDate)
    if days is None or days <= 0:
        return None

    nOfProcessedDays = len(events['datetime'].dt.normalize().unique())
    flowEfficiency = int(round(float(nOfProcessedDays) / days, 2) * 100)

    return flowEfficiency
<|endoftext|>