<commit_before>###############################################################################
##
## Copyright (C) 2011-2013, NYU-Poly.
## Copyright (C) 2006-2011, University of Utah.
## All rights reserved.
## Contact: contact@vistrails.org
##
## This file is part of VisTrails.
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are met:
##
##  - Redistributions of source code must retain the above copyright notice,
##    this list of conditions and the following disclaimer.
##  - Redistributions in binary form must reproduce the above copyright
##    notice, this list of conditions and the following disclaimer in the
##    documentation and/or other materials provided with the distribution.
##  - Neither the name of the University of Utah nor the names of its
##    contributors may be used to endorse or promote products derived from
##    this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
## THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
## OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
## OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
## ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
##
###############################################################################

import copy

from vistrails.core import debug
from vistrails.core.log.workflow_exec import WorkflowExec
from vistrails.core.log.module_exec import ModuleExec
from vistrails.core.log.loop_exec import LoopExec
from vistrails.core.log.group_exec import GroupExec
from vistrails.core.log.machine import Machine
from vistrails.core.modules.sub_module import Group, Abstraction
from vistrails.core.vistrail.annotation import Annotation
from vistrails.core.vistrail.pipeline import Pipeline
from vistrails.core.vistrail.vistrail import Vistrail
import vistrails.core.system

@apply
class DummyLogController(object):
    """DummyLogger is a class that has the entire interface for a logger
    but simply ignores the calls."""
    def start_workflow_execution(self, *args, **kwargs): return self
    def recursing(self, *args, **kwargs): return self
    def finish_workflow_execution(self, *args, **kwargs): pass
    def add_exec(self, *args, **kwargs): pass
    def start_execution(self, *args, **kwargs): pass
    def start_loop_execution(self, *args, **kwargs): pass
    def finish_loop_execution(self, *args, **kwargs): pass
    def finish_execution(self, *args, **kwargs): pass
    def insert_module_annotations(self, *args, **kwargs): pass
    def insert_workflow_exec_annotations(self, *args, **kwargs): pass
    def add_machine(self, *args, **kwargs): return -1
    def __call__(self): return self

class LogController(object):
    """The top-level log controller.

    This holds a log.
    """
    local_machine = Machine(
            id=-1,
            name=vistrails.core.system.current_machine(),
            os=vistrails.core.system.systemType,
            architecture=vistrails.core.system.current_architecture(),
            processor=vistrails.core.system.current_processor(),
            ram=vistrails.core.system.guess_total_memory())

    def __init__(self, log, machine=None):
        self.log = log
        self.module_execs = {}      # vistrails_module -> *Exec
        self.parent_execs = {}      # vistrails_module -> *Exec
        self.children_execs = {}    # vistrails_module -> [*Exec]
        if machine is not None:
            self.machine = machine
        else:
            self.machine = copy.copy(self.local_machine)
            self.machine.id = self.log.id_scope.getNewId(Machine.vtType)

    def _create_module_exec(self, module, module_id, module_name,
                            cached):
        m_exec_id = self.log.id_scope.getNewId(ModuleExec.vtType)
        module_exec = ModuleExec(id=m_exec_id,
                                 machine_id=self.machine.id,
                                 module_id=module_id,
                                 module_name=module_name,
                                 cached=cached,
                                 ts_start=vistrails.core.system.current_time(),
                                 completed=0)
        return module_exec

    def _create_group_exec(self, group, module_id, group_name, cached):
        g_exec_id = self.log.id_scope.getNewId(GroupExec.vtType)
        if isinstance(group, Abstraction):
            group_type = 'SubWorkflow'
        else:
            group_type = 'Group'
        group_exec = GroupExec(id=g_exec_id,
                               machine_id=self.machine.id,
                               module_id=module_id,
                               group_name=group_name,
                               group_type=group_type,
                               cached=cached,
                               ts_start=vistrails.core.system.current_time(),
                               completed=0)
        return group_exec

    def _create_loop_exec(self, iteration):
        l_exec_id = self.log.id_scope.getNewId(LoopExec.vtType)
        loop_exec = LoopExec(id=l_exec_id,
                             iteration=iteration,
                             ts_start=vistrails.core.system.current_time())
        return loop_exec

    def start_workflow_execution(self, parent_exec,
                                 vistrail=None, pipeline=None,
                                 currentVersion=None):
        """Signals the start of the execution of a pipeline.
        """
        return LogWorkflowExecController(self.log, self.machine, parent_exec,
                                         vistrail, pipeline, currentVersion)

class LogWorkflowController(LogController):
    """A log controller for a specific workflow execution.

    You get one of these by calling LogController#start_workflow_execution() or
    LogWorkflowController#recursing(). You can then add execution items through
    it.

    How does this work:
      * the interpreter sets a 'logging' attribute on summoned Module objects
        before starting the execution. Through it, the module can log events.
      * the interpreter directly uses the logger to record exceptions from the
        pipeline
      * while they are executing, the logger keeps a table mapping a Module to:
         - module_execs has the execution entry for that module
         - parent_execs is set via other modules to remember where the soon-to-
           be-created module_exec should be added (for example, loop modules
           and Group sets parent_exec on their dependent modules). If
           parent_exec is not set, the parent exec or WorkflowExec will be used
         - children_exec is a list of ongoing exec items, children of
           module_exec, such as LoopExec. They are kept their to be marked as
           finished with the same error as the module if it fails before they
           end
    """
    def __init__(self, log, machine, parent_exec, workflow_exec):
        super(LogWorkflowController, self).__init__(log, machine)
        self.parent_exec = parent_exec
        self.workflow_exec = workflow_exec

    def recursing(self, parent_exec):
        """Enters a recursing execution.

        This returns a new log controller object for that execution context.
        """
        if parent_exec in self.module_execs:
            parent_exec = self.module_execs[parent_exec]
        return LogWorkflowController(self.log, self.machine, parent_exec,
                                     self.workflow_exec)

    def start_execution(self, module, module_id, module_name, cached=0):
        """Signals the start of the execution of a module (before compute).
        """
        if isinstance(module, Group):
            module_exec = self._create_group_exec(module, module_id,
                                                 module_name, cached)
        else:
            module_exec = self._create_module_exec(module, module_id,
                                                   module_name, cached)
        if module in self.module_execs is not None:
            debug.warning(
                    "%s#start_execution(module=%r, module_id=%r, "
                    "module_name=%r, cached=%r): module already has a "
                    "module_exec! Overwriting" % (
                    type(self).__name__,
                    module, module_id, module_name, cached))
        self.module_execs[module] = module_exec
        for parent_exec in (self.parent_execs.get(module), self.parent_exec,
                            self.workflow_exec):
            if parent_exec is not None:
                parent_exec.add_item_exec(module_exec)
                return
        assert False

    def start_loop_execution(self, loop_module, looped_module,
                             iteration, total_iterations=None):
        """Registers a looped module.
        """
        loop_exec = self._create_loop_exec(iteration)
        for parent_exec in (self.module_execs.get(loop_module),
                            self.parent_exec):
            if parent_exec is not None:
                parent_exec.add_loop_exec(loop_exec)
                break
        else:
            self.workflow_exec.add_item_exec(loop_exec)
        self.parent_execs[looped_module] = loop_exec
        self.children_execs.setdefault(loop_module, set()).add(loop_exec)

    def finish_loop_execution(self, loop_module, looped_module):
        """Signals that we are done looping.
        """
        loop_exec = self.parent_execs.pop(looped_module)
        assert loop_exec is not None

        loop_exec.ts_end = vistrails.core.system.current_time()
        loop_exec.completed = 1
        self.children_execs.setdefault(loop_module, set()).discard(loop_exec)

    def finish_execution(self, module, error, errorTrace=None, suspended=False):
        """Signals the end of the execution of a module.

        Called by a module after succeeded of suspended, or called by the
        interpreter after an exception.
        """
        module_exec = self.module_execs.pop(module, None)
        if module_exec is None:
            # The module can finish execution without starting (if it was
            # suspended, etc...)
            return
        module_exec.ts_end = vistrails.core.system.current_time()
        if suspended:
            module_exec.completed = -2
            module_exec.error = error
        elif error:
            module_exec.completed = -1
            module_exec.error = error
            if errorTrace:
                a_id = self.log.id_scope.getNewId(Annotation.vtType)
                annotation = Annotation(id=a_id,
                                        key="errorTrace",
                                        value=errorTrace)
                module_exec.add_annotation(annotation)
        else:
            module_exec.completed = 1

        for child in self.children_execs.pop(module, ()):
            child.ts_end = vistrails.core.system.current_time()
            if suspended:
                child.completed = -2
                child.error = error
            elif not error:
                child.completed = 1
            else:
                child.completed = -1
                child.error = error

    def insert_module_annotations(self, module, a_dict):
        """Adds an annotation on the execution object for this module.
        """
        for k, v in a_dict.iteritems():
            a_id = self.log.id_scope.getNewId(Annotation.vtType)
            annotation = Annotation(id=a_id,
                                    key=k,
                                    value=v)
            self.module_execs[module].add_annotation(annotation)

    def insert_workflow_exec_annotations(self, a_dict):
        """Adds an annotation on the whole workflow log object.

        The information is not associated with the execution of a specific
        module but of the whole pipeline.
        """
        if self.workflow_exec:
            for k, v in a_dict.iteritems():
                a_id = self.log.id_scope.getNewId(Annotation.vtType)
                annotation = Annotation(id=a_id,
                                        key=k,
                                        value=v)
                self.workflow_exec.add_annotation(annotation)

    def add_machine(self, machine):
        machine.id = self.log.id_scope.getNewId(Machine.vtType)
        self.workflow_exec.add_machine(machine)
        return machine.id

    def add_exec(self, exec_):
        self.workflow_exec.add_item_exec(exec_)

class LogWorkflowExecController(LogWorkflowController):
    """Top-level LogWorkflowController, returned by start_workflow_execution().

    This one has finish_workflow_execution(). The LogWorkflowController,
    obtained through recursing(), don't.
    """
    def __init__(self, log, machine, parent_exec, vistrail=None, pipeline=None,
                 currentVersion=None):
        if vistrail is not None:
            parent_type = Vistrail.vtType
            parent_id = vistrail.id
        else:
            parent_type = Pipeline.vtType
            parent_id = pipeline.id

        wf_exec_id = log.id_scope.getNewId(WorkflowExec.vtType)
        if vistrail is not None:
            session = vistrail.current_session
        else:
            session = None
        workflow_exec = WorkflowExec(
                id=wf_exec_id,
                user=vistrails.core.system.current_user(),
                ip=vistrails.core.system.current_ip(),
                vt_version=vistrails.core.system.vistrails_version(),
                ts_start=vistrails.core.system.current_time(),
                parent_type=parent_type,
                parent_id=parent_id,
                parent_version=currentVersion,
                completed=0,
                session=session,
                machines=[machine])
        log.add_workflow_exec(workflow_exec)

        super(LogWorkflowExecController, self).__init__(log, machine, parent_exec, workflow_exec)

    def finish_workflow_execution(self, errors, suspended=False):
        """Signals the end of the execution of a pipeline.
        """
        self.workflow_exec.ts_end = vistrails.core.system.current_time()
        if suspended:
            self.workflow_exec.completed = -2
        elif len(errors) > 0:
            self.workflow_exec.completed = -1
        else:
            self.workflow_exec.completed = 1
<commit_msg>Updates controller<commit_after>###############################################################################
##
## Copyright (C) 2011-2013, NYU-Poly.
## Copyright (C) 2006-2011, University of Utah.
## All rights reserved.
## Contact: contact@vistrails.org
##
## This file is part of VisTrails.
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are met:
##
##  - Redistributions of source code must retain the above copyright notice,
##    this list of conditions and the following disclaimer.
##  - Redistributions in binary form must reproduce the above copyright
##    notice, this list of conditions and the following disclaimer in the
##    documentation and/or other materials provided with the distribution.
##  - Neither the name of the University of Utah nor the names of its
##    contributors may be used to endorse or promote products derived from
##    this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
## THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
## OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
## OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
## ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
##
###############################################################################

import copy

from vistrails.core import debug
from vistrails.core.log.workflow_exec import WorkflowExec
from vistrails.core.log.module_exec import ModuleExec
from vistrails.core.log.loop_exec import LoopExec, LoopIteration
from vistrails.core.log.group_exec import GroupExec
from vistrails.core.log.machine import Machine
from vistrails.core.modules.sub_module import Group, Abstraction
from vistrails.core.vistrail.annotation import Annotation
from vistrails.core.vistrail.pipeline import Pipeline
from vistrails.core.vistrail.vistrail import Vistrail
import vistrails.core.system

@apply
class DummyLogController(object):
    """DummyLogger is a class that has the entire interface for a logger
    but simply ignores the calls."""
    def start_workflow_execution(self, *args, **kwargs): return self
    def recursing(self, *args, **kwargs): return self
    def finish_workflow_execution(self, *args, **kwargs): pass
    def add_exec(self, *args, **kwargs): pass
    def start_execution(self, *args, **kwargs): pass
    def start_loop_execution(self, *args, **kwargs): return self
    def finish_loop_execution(self, *args, **kwargs): pass
    def finish_execution(self, *args, **kwargs): pass
    def insert_module_annotations(self, *args, **kwargs): pass
    def insert_workflow_exec_annotations(self, *args, **kwargs): pass
    def add_machine(self, *args, **kwargs): return -1
    def __call__(self): return self

class LogController(object):
    """The top-level log controller.

    This holds a log.
    """
    local_machine = Machine(
            id=-1,
            name=vistrails.core.system.current_machine(),
            os=vistrails.core.system.systemType,
            architecture=vistrails.core.system.current_architecture(),
            processor=vistrails.core.system.current_processor(),
            ram=vistrails.core.system.guess_total_memory())

    def __init__(self, log, machine=None):
        self.log = log
        self.module_execs = {}      # vistrails_module -> *Exec
        self.parent_execs = {}      # vistrails_module -> *Exec
        self.children_execs = {}    # vistrails_module -> [*Exec]
        if machine is not None:
            self.machine = machine
        else:
            self.machine = copy.copy(self.local_machine)
            self.machine.id = self.log.id_scope.getNewId(Machine.vtType)

    def _create_module_exec(self, module, module_id, module_name,
                            cached):
        m_exec_id = self.log.id_scope.getNewId(ModuleExec.vtType)
        module_exec = ModuleExec(id=m_exec_id,
                                 machine_id=self.machine.id,
                                 module_id=module_id,
                                 module_name=module_name,
                                 cached=cached,
                                 ts_start=vistrails.core.system.current_time(),
                                 completed=0)
        return module_exec

    def _create_group_exec(self, group, module_id, group_name, cached):
        g_exec_id = self.log.id_scope.getNewId(GroupExec.vtType)
        if isinstance(group, Abstraction):
            group_type = 'SubWorkflow'
        else:
            group_type = 'Group'
        group_exec = GroupExec(id=g_exec_id,
                               machine_id=self.machine.id,
                               module_id=module_id,
                               group_name=group_name,
                               group_type=group_type,
                               cached=cached,
                               ts_start=vistrails.core.system.current_time(),
                               completed=0)
        return group_exec

    def _create_loop_exec(self):
        l_exec_id = self.log.id_scope.getNewId(LoopExec.vtType)
        loop_exec = LoopExec(id=l_exec_id,
                             ts_start=vistrails.core.system.current_time())
        return loop_exec

    def start_workflow_execution(self, parent_exec,
                                 vistrail=None, pipeline=None,
                                 currentVersion=None):
        """Signals the start of the execution of a pipeline.
        """
        return LogWorkflowExecController(self.log, self.machine, parent_exec,
                                         vistrail, pipeline, currentVersion)

class LogLoopController(object):
    def __init__(self, controller, loop_exec, loop_module):
        self.controller = controller
        self.loop_exec = loop_exec
        self.loop_module = loop_module

    def _create_loop_iteration(self, iteration):
        l_iteration_id = self.controller.log.id_scope.getNewId(
                LoopIteration.vtType)
        loop_iteration = LoopIteration(id=l_iteration_id,
                                       ts_start=vistrails.core.system.current_time(),
                                       iteration=iteration)
        return loop_iteration

    def finish_loop_execution(self):
        """Signals that we are done looping.
        """
        self.loop_exec.ts_end = vistrails.core.system.current_time()
        try:
            execs = self.controller.children_execs[self.loop_module]
            execs.discard(self.loop_exec)
        except KeyError:
            pass

    def start_iteration(self, looped_module, iteration):
        """Signals that we are executing a module as an iteration of the loop.
        """
        loop_iteration = self._create_loop_iteration(iteration)
        self.loop_exec.add_loop_iteration(loop_iteration)
        self.controller.parent_execs[looped_module] = loop_iteration

    def finish_iteration(self, looped_module):
        """Signals that the iteration is done.
        """
        loop_iteration = self.controller.parent_execs.pop(looped_module)
        assert loop_iteration is not None

        loop_iteration.ts_end = vistrails.core.system.current_time()
        loop_iteration.completed = 1

class LogWorkflowController(LogController):
    """A log controller for a specific workflow execution.

    You get one of these by calling LogController#start_workflow_execution() or
    LogWorkflowController#recursing(). You can then add execution items through
    it.

    How does this work:
      * the interpreter sets a 'logging' attribute on summoned Module objects
        before starting the execution. Through it, the module can log events.
      * the interpreter directly uses the logger to record exceptions from the
        pipeline
      * while they are executing, the logger keeps a table mapping a Module to:
         - module_execs has the execution entry for that module
         - parent_execs is set via other modules to remember where the soon-to-
           be-created module_exec should be added (for example, loop modules
           and Group sets parent_exec on their dependent modules). If
           parent_exec is not set, the parent exec or WorkflowExec will be used
         - children_exec is a list of ongoing exec items, children of
           module_exec, such as LoopExec. They are kept their to be marked as
           finished with the same error as the module if it fails before they
           end
    """
    def __init__(self, log, machine, parent_exec, workflow_exec):
        super(LogWorkflowController, self).__init__(log, machine)
        self.parent_exec = parent_exec
        self.workflow_exec = workflow_exec

    def recursing(self, parent_exec):
        """Enters a recursing execution.

        This returns a new log controller object for that execution context.
        """
        if parent_exec in self.module_execs:
            parent_exec = self.module_execs[parent_exec]
        return LogWorkflowController(self.log, self.machine, parent_exec,
                                     self.workflow_exec)

    def start_execution(self, module, module_id, module_name, cached=0):
        """Signals the start of the execution of a module (before compute).
        """
        if isinstance(module, Group):
            module_exec = self._create_group_exec(module, module_id,
                                                 module_name, cached)
        else:
            module_exec = self._create_module_exec(module, module_id,
                                                   module_name, cached)
        if module in self.module_execs is not None:
            debug.warning(
                    "%s#start_execution(module=%r, module_id=%r, "
                    "module_name=%r, cached=%r): module already has a "
                    "module_exec! Overwriting" % (
                    type(self).__name__,
                    module, module_id, module_name, cached))
        self.module_execs[module] = module_exec
        for parent_exec in (self.parent_execs.get(module), self.parent_exec,
                            self.workflow_exec):
            if parent_exec is not None:
                parent_exec.add_item_exec(module_exec)
                return
        assert False

    def start_loop_execution(self, loop_module, total_iterations=None):
        """Starts a loop.
        """
        loop_exec = self._create_loop_exec()
        for parent_exec in (self.module_execs.get(loop_module),
                            self.parent_exec):
            if parent_exec is not None:
                parent_exec.add_loop_exec(loop_exec)
                break
        else:
            self.workflow_exec.add_item_exec(loop_exec)
        self.children_execs.setdefault(loop_module, set()).add(loop_exec)
        return LogLoopController(self, loop_exec, loop_module)

    def finish_execution(self, module, error, errorTrace=None, suspended=False):
        """Signals the end of the execution of a module.

        Called by a module after succeeded of suspended, or called by the
        interpreter after an exception.
        """
        module_exec = self.module_execs.pop(module, None)
        if module_exec is None:
            # The module can finish execution without starting (if it was
            # suspended, etc...)
            return
        module_exec.ts_end = vistrails.core.system.current_time()
        if suspended:
            module_exec.completed = -2
            module_exec.error = error
        elif error:
            module_exec.completed = -1
            module_exec.error = error
            if errorTrace:
                a_id = self.log.id_scope.getNewId(Annotation.vtType)
                annotation = Annotation(id=a_id,
                                        key="errorTrace",
                                        value=errorTrace)
                module_exec.add_annotation(annotation)
        else:
            module_exec.completed = 1

        for child in self.children_execs.pop(module, ()):
            child.ts_end = vistrails.core.system.current_time()
            if suspended:
                child.completed = -2
                child.error = error
            elif not error:
                child.completed = 1
            else:
                child.completed = -1
                child.error = error

    def insert_module_annotations(self, module, a_dict):
        """Adds an annotation on the execution object for this module.
        """
        for k, v in a_dict.iteritems():
            a_id = self.log.id_scope.getNewId(Annotation.vtType)
            annotation = Annotation(id=a_id,
                                    key=k,
                                    value=v)
            self.module_execs[module].add_annotation(annotation)

    def insert_workflow_exec_annotations(self, a_dict):
        """Adds an annotation on the whole workflow log object.

        The information is not associated with the execution of a specific
        module but of the whole pipeline.
        """
        if self.workflow_exec:
            for k, v in a_dict.iteritems():
                a_id = self.log.id_scope.getNewId(Annotation.vtType)
                annotation = Annotation(id=a_id,
                                        key=k,
                                        value=v)
                self.workflow_exec.add_annotation(annotation)

    def add_machine(self, machine):
        machine.id = self.log.id_scope.getNewId(Machine.vtType)
        self.workflow_exec.add_machine(machine)
        return machine.id

    def add_exec(self, exec_):
        self.workflow_exec.add_item_exec(exec_)

class LogWorkflowExecController(LogWorkflowController):
    """Top-level LogWorkflowController, returned by start_workflow_execution().

    This one has finish_workflow_execution(). The LogWorkflowController,
    obtained through recursing(), don't.
    """
    def __init__(self, log, machine, parent_exec, vistrail=None, pipeline=None,
                 currentVersion=None):
        if vistrail is not None:
            parent_type = Vistrail.vtType
            parent_id = vistrail.id
        else:
            parent_type = Pipeline.vtType
            parent_id = pipeline.id

        wf_exec_id = log.id_scope.getNewId(WorkflowExec.vtType)
        if vistrail is not None:
            session = vistrail.current_session
        else:
            session = None
        workflow_exec = WorkflowExec(
                id=wf_exec_id,
                user=vistrails.core.system.current_user(),
                ip=vistrails.core.system.current_ip(),
                vt_version=vistrails.core.system.vistrails_version(),
                ts_start=vistrails.core.system.current_time(),
                parent_type=parent_type,
                parent_id=parent_id,
                parent_version=currentVersion,
                completed=0,
                session=session,
                machines=[machine])
        log.add_workflow_exec(workflow_exec)

        super(LogWorkflowExecController, self).__init__(log, machine, parent_exec, workflow_exec)

    def finish_workflow_execution(self, errors, suspended=False):
        """Signals the end of the execution of a pipeline.
        """
        self.workflow_exec.ts_end = vistrails.core.system.current_time()
        if suspended:
            self.workflow_exec.completed = -2
        elif len(errors) > 0:
            self.workflow_exec.completed = -1
        else:
            self.workflow_exec.completed = 1
<|endoftext|>


<commit_before>from copy import copy
from typing import Any, Dict, Tuple, Optional
from datetime import datetime
from tzlocal import get_localzone

from vnpy.event import Event, EventEngine
from vnpy.trader.utility import extract_vt_symbol
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.object import (
    OrderRequest, CancelRequest, SubscribeRequest,
    ContractData, OrderData, TradeData, TickData,
    LogData, PositionData
)
from vnpy.trader.event import (
    EVENT_ORDER,
    EVENT_TRADE,
    EVENT_TICK,
    EVENT_POSITION,
    EVENT_CONTRACT,
    EVENT_LOG
)
from vnpy.trader.constant import (
    Status,
    OrderType,
    Direction,
    Offset
)

LOCAL_TZ = get_localzone()
APP_NAME = "PaperAccount"
GATEWAY_NAME = "PAPER"

class PaperEngine(BaseEngine):
    """"""

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__(main_engine, event_engine, APP_NAME)

        self.slippage: int = 0
        self.order_count: int = 100000

        self.active_orders: Dict[str, Dict[str, OrderData]] = {}
        self.gateway_map: Dict[str, str] = {}
        self.ticks: Dict[str, TickData] = {}
        self.positions: Dict[Tuple[str, Direction], PositionData] = {}

        self._subscribe = main_engine.subscribe

        main_engine.subscribe = self.subscribe
        main_engine.send_order = self.send_order
        main_engine.cancel_order = self.cancel_order

        self.register_event()

    def register_event(self):
        """"""
        self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)
        self.event_engine.register(EVENT_TICK, self.process_tick_event)

    def process_contract_event(self, event: Event) -> None:
        """"""
        contract: ContractData = event.data
        self.gateway_map[contract.vt_symbol] = contract.gateway_name
        contract.gateway_name = GATEWAY_NAME

    def process_tick_event(self, event: Event) -> None:
        """"""
        tick: TickData = event.data
        tick.gateway_name = GATEWAY_NAME

        self.ticks[tick.vt_symbol] = tick

        if tick.vt_symbol not in self.active_orders:
            return

        active_orders = self.active_orders[tick.vt_symbol]
        if not active_orders:
            return

        for orderid, order in list(active_orders.items()):
            self.cross_order(order, tick)

            if not order.is_active():
                active_orders.pop(orderid)

    def subscribe(self, req: SubscribeRequest, gateway_name: str) -> None:
        """"""
        original_gateway_name = self.gateway_map.get(req.vt_symbol, "")
        if original_gateway_name:
            self._subscribe(req, original_gateway_name)

    def send_order(self, req: OrderRequest, gateway_name: str) -> str:
        """"""
        contract: ContractData = self.main_engine.get_contract(req.vt_symbol)
        if not contract:
            self.write_log(f"委托失败，找不到该合约{req.vt_symbol}")
            return ""

        self.order_count += 1
        now = datetime.now().strftime("%y%m%d%H%M%S")
        orderid = now + str(self.order_count)
        vt_orderid = f"{GATEWAY_NAME}.{orderid}"

        # Put simulated order update event from gateway
        order = req.create_order_data(orderid, GATEWAY_NAME)
        self.put_event(EVENT_ORDER, copy(order))

        # Check if order is valid
        updated_position = self.check_order_valid(order, contract)

        # Put simulated order update event from exchange
        if order.status != Status.REJECTED:
            order.datetime = datetime.now(LOCAL_TZ)
            order.status = Status.NOTTRADED
            active_orders = self.active_orders.setdefault(order.vt_symbol, {})
            active_orders[orderid] = order

        self.put_event(EVENT_ORDER, copy(order))

        # Update position frozen for close order
        if updated_position:
            self.put_event(EVENT_POSITION, copy(updated_position))

        return vt_orderid

    def cancel_order(self, req: CancelRequest, gateway_name: str) -> None:
        """"""
        active_orders: Dict[str, OrderRequest] = self.active_orders[req.vt_symbol]

        if req.orderid in active_orders:
            order: OrderData = active_orders.pop(req.orderid)
            order.status = Status.CANCELLED
            self.put_event(EVENT_ORDER, copy(order))

            # Free frozen position volume
            if order.offset == Offset.OPEN:
                return

            if order.direction == Direction.LONG:
                position = self.get_position(order.vt_symbol, Direction.SHORT)
            else:
                position = self.get_position(order.vt_symbol, Direction.LONG)
            position.frozen -= order.volume

            self.put_event(EVENT_POSITION, copy(position))

    def put_event(self, event_type: str, data: Any) -> None:
        """"""
        event = Event(event_type, data)
        self.event_engine.put(event)

    def check_order_valid(self, order: OrderData, contract: ContractData) -> Optional[PositionData]:
        """"""
        # Reject unsupported order type
        if order.type in {OrderType.FAK, OrderType.FOK, OrderType.RFQ}:
            order.status = Status.REJECTED
        elif order.type == OrderType.STOP and not contract.stop_supported:
            order.status = Status.REJECTED

        if order.status == Status.REJECTED:
            self.write_log(f"委托被拒单，不支持的委托类型{order.type.value}")

        # Reject close order if no more available position
        if contract.net_position or order.offset == Offset.OPEN:
            return

        if order.direction == Direction.LONG:
            short_position = self.get_position(order.vt_symbol, Direction.SHORT)
            available = short_position.volume - short_position.frozen

            if order.volume > available:
                order.status = Status.REJECTED
            else:
                short_position.frozen += order.volume
                return short_position
        else:
            long_position = self.get_position(order.vt_symbol, Direction.LONG)
            available = long_position.volume - long_position.frozen

            if order.volume > available:
                order.status = Status.REJECTED
            else:
                long_position.frozen += order.volume
                return long_position

            if order.status == Status.REJECTED:
                self.write_log(f"委托被拒单，可平仓位不足")

    def cross_order(self, order: OrderData, tick: TickData):
        """"""
        contract = self.main_engine.get_contract(order.vt_symbol)

        trade_price = 0

        # Cross market order immediately after received
        if order.type == OrderType.MARKET:
            if order.direction == Direction.LONG:
                trade_price = tick.ask_price_1 + self.slippage * contract.pricetick
            else:
                trade_price = tick.bid_price_1 - self.slippage * contract.pricetick
        # Cross limit order only if price touched
        elif order.type == OrderType.LIMIT:
            if order.direction == Direction.LONG:
                if order.price >= tick.ask_price_1:
                    trade_price = tick.ask_price_1
            else:
                if order.price <= tick.bid_price_1:
                    trade_price = tick.bid_price_1
        # Cross limit order only if price broken
        elif order.type == OrderType.STOP:
            if order.direction == Direction.LONG:
                if tick.ask_price_1 >= order.price:
                    trade_price = tick.ask_price_1 + self.slippage * contract.pricetick
            else:
                if tick.bid_price_1 <= order.price:
                    trade_price = tick.bid_price_1 - self.slippage * contract.pricetick

        if trade_price:
            order.status = Status.ALLTRADED
            order.traded = order.volume
            self.put_event(EVENT_ORDER, order)

            trade = TradeData(
                symbol=order.symbol,
                exchange=order.exchange,
                orderid=order.orderid,
                tradeid=order.orderid,
                direction=order.direction,
                offset=order.offset,
                price=trade_price,
                volume=order.volume,
                datetime=datetime.now(LOCAL_TZ),
                gateway_name=order.gateway_name
            )
            self.put_event(EVENT_TRADE, trade)

            self.update_position(trade, contract)

    def update_position(self, trade: TradeData, contract: ContractData):
        """"""
        vt_symbol = trade.vt_symbol

        # Net position mode
        if contract.net_position:
            position = self.get_position(vt_symbol, Direction.NET)

            if trade.direction == Direction.LONG:
                position.volume += trade.volume
            else:
                position.volume -= trade.volume

            self.put_event(EVENT_POSITION, copy(position))
        # Long/Short position mode
        else:
            long_position = self.get_position(vt_symbol, Direction.LONG)
            short_position = self.get_position(vt_symbol, Direction.SHORT)

            if trade.direction == Direction.LONG:
                if trade.offset == Offset.OPEN:
                    long_position.volume += trade.volume
                else:
                    short_position.volume -= trade.volume
                    short_position.frozen -= trade.volume
            else:
                if trade.offset == Offset.OPEN:
                    short_position.volume += trade.volume
                else:
                    long_position.volume -= trade.volume
                    long_position.frozen -= trade.volume

            self.put_event(EVENT_POSITION, long_position)
            self.put_event(EVENT_POSITION, short_position)

    def get_position(self, vt_symbol: str, direction: Direction):
        """"""
        key = (vt_symbol, direction)

        if key in self.positions:
            return self.positions[key]
        else:
            symbol, exchange = extract_vt_symbol(vt_symbol)
            position = PositionData(
                symbol=symbol,
                exchange=exchange,
                direction=direction,
                gateway_name=GATEWAY_NAME
            )

            self.positions[key] = position
            return position

    def write_log(self, msg: str) -> None:
        """"""
        log = LogData(msg=msg, gateway_name=GATEWAY_NAME)
        self.put_event(EVENT_LOG, log)
<commit_msg>[Add] save/load position data function<commit_after>from copy import copy
from typing import Any, Dict, Tuple, Optional
from datetime import datetime
from tzlocal import get_localzone

from vnpy.event import Event, EventEngine
from vnpy.trader.utility import extract_vt_symbol, save_json, load_json
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.object import (
    OrderRequest, CancelRequest, SubscribeRequest,
    ContractData, OrderData, TradeData, TickData,
    LogData, PositionData
)
from vnpy.trader.event import (
    EVENT_ORDER,
    EVENT_TRADE,
    EVENT_TICK,
    EVENT_POSITION,
    EVENT_CONTRACT,
    EVENT_LOG
)
from vnpy.trader.constant import (
    Status,
    OrderType,
    Direction,
    Offset
)

LOCAL_TZ = get_localzone()
APP_NAME = "PaperAccount"
GATEWAY_NAME = "PAPER"

class PaperEngine(BaseEngine):
    """"""
    setting_filename = "paper_account_setting.json"
    data_filename = "paper_account_data.json"

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__(main_engine, event_engine, APP_NAME)

        self.slippage: int = 0
        self.order_count: int = 100000

        self.active_orders: Dict[str, Dict[str, OrderData]] = {}
        self.gateway_map: Dict[str, str] = {}
        self.ticks: Dict[str, TickData] = {}
        self.positions: Dict[Tuple[str, Direction], PositionData] = {}

        self._subscribe = main_engine.subscribe

        main_engine.subscribe = self.subscribe
        main_engine.send_order = self.send_order
        main_engine.cancel_order = self.cancel_order

        self.register_event()
        self.load_data()

    def register_event(self):
        """"""
        self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)
        self.event_engine.register(EVENT_TICK, self.process_tick_event)

    def process_contract_event(self, event: Event) -> None:
        """"""
        contract: ContractData = event.data
        self.gateway_map[contract.vt_symbol] = contract.gateway_name
        contract.gateway_name = GATEWAY_NAME

        for direciton in Direction:
            key = (contract.vt_symbol, direciton)
            if key in self.positions:
                position = self.positions[key]
                self.put_event(EVENT_POSITION, position)

    def process_tick_event(self, event: Event) -> None:
        """"""
        tick: TickData = event.data
        tick.gateway_name = GATEWAY_NAME

        self.ticks[tick.vt_symbol] = tick

        if tick.vt_symbol not in self.active_orders:
            return

        active_orders = self.active_orders[tick.vt_symbol]
        if not active_orders:
            return

        for orderid, order in list(active_orders.items()):
            self.cross_order(order, tick)

            if not order.is_active():
                active_orders.pop(orderid)

    def subscribe(self, req: SubscribeRequest, gateway_name: str) -> None:
        """"""
        original_gateway_name = self.gateway_map.get(req.vt_symbol, "")
        if original_gateway_name:
            self._subscribe(req, original_gateway_name)

    def send_order(self, req: OrderRequest, gateway_name: str) -> str:
        """"""
        contract: ContractData = self.main_engine.get_contract(req.vt_symbol)
        if not contract:
            self.write_log(f"委托失败，找不到该合约{req.vt_symbol}")
            return ""

        self.order_count += 1
        now = datetime.now().strftime("%y%m%d%H%M%S")
        orderid = now + str(self.order_count)
        vt_orderid = f"{GATEWAY_NAME}.{orderid}"

        # Put simulated order update event from gateway
        order = req.create_order_data(orderid, GATEWAY_NAME)
        self.put_event(EVENT_ORDER, copy(order))

        # Check if order is valid
        updated_position = self.check_order_valid(order, contract)

        # Put simulated order update event from exchange
        if order.status != Status.REJECTED:
            order.datetime = datetime.now(LOCAL_TZ)
            order.status = Status.NOTTRADED
            active_orders = self.active_orders.setdefault(order.vt_symbol, {})
            active_orders[orderid] = order

        self.put_event(EVENT_ORDER, copy(order))

        # Update position frozen for close order
        if updated_position:
            self.put_event(EVENT_POSITION, copy(updated_position))

        return vt_orderid

    def cancel_order(self, req: CancelRequest, gateway_name: str) -> None:
        """"""
        active_orders: Dict[str, OrderRequest] = self.active_orders[req.vt_symbol]

        if req.orderid in active_orders:
            order: OrderData = active_orders.pop(req.orderid)
            order.status = Status.CANCELLED
            self.put_event(EVENT_ORDER, copy(order))

            # Free frozen position volume
            if order.offset == Offset.OPEN:
                return

            if order.direction == Direction.LONG:
                position = self.get_position(order.vt_symbol, Direction.SHORT)
            else:
                position = self.get_position(order.vt_symbol, Direction.LONG)
            position.frozen -= order.volume

            self.put_event(EVENT_POSITION, copy(position))

    def put_event(self, event_type: str, data: Any) -> None:
        """"""
        event = Event(event_type, data)
        self.event_engine.put(event)

    def check_order_valid(self, order: OrderData, contract: ContractData) -> Optional[PositionData]:
        """"""
        # Reject unsupported order type
        if order.type in {OrderType.FAK, OrderType.FOK, OrderType.RFQ}:
            order.status = Status.REJECTED
        elif order.type == OrderType.STOP and not contract.stop_supported:
            order.status = Status.REJECTED

        if order.status == Status.REJECTED:
            self.write_log(f"委托被拒单，不支持的委托类型{order.type.value}")

        # Reject close order if no more available position
        if contract.net_position or order.offset == Offset.OPEN:
            return

        if order.direction == Direction.LONG:
            short_position = self.get_position(order.vt_symbol, Direction.SHORT)
            available = short_position.volume - short_position.frozen

            if order.volume > available:
                order.status = Status.REJECTED
            else:
                short_position.frozen += order.volume
                return short_position
        else:
            long_position = self.get_position(order.vt_symbol, Direction.LONG)
            available = long_position.volume - long_position.frozen

            if order.volume > available:
                order.status = Status.REJECTED
            else:
                long_position.frozen += order.volume
                return long_position

            if order.status == Status.REJECTED:
                self.write_log(f"委托被拒单，可平仓位不足")

    def cross_order(self, order: OrderData, tick: TickData):
        """"""
        contract = self.main_engine.get_contract(order.vt_symbol)

        trade_price = 0

        # Cross market order immediately after received
        if order.type == OrderType.MARKET:
            if order.direction == Direction.LONG:
                trade_price = tick.ask_price_1 + self.slippage * contract.pricetick
            else:
                trade_price = tick.bid_price_1 - self.slippage * contract.pricetick
        # Cross limit order only if price touched
        elif order.type == OrderType.LIMIT:
            if order.direction == Direction.LONG:
                if order.price >= tick.ask_price_1:
                    trade_price = tick.ask_price_1
            else:
                if order.price <= tick.bid_price_1:
                    trade_price = tick.bid_price_1
        # Cross limit order only if price broken
        elif order.type == OrderType.STOP:
            if order.direction == Direction.LONG:
                if tick.ask_price_1 >= order.price:
                    trade_price = tick.ask_price_1 + self.slippage * contract.pricetick
            else:
                if tick.bid_price_1 <= order.price:
                    trade_price = tick.bid_price_1 - self.slippage * contract.pricetick

        if trade_price:
            order.status = Status.ALLTRADED
            order.traded = order.volume
            self.put_event(EVENT_ORDER, order)

            trade = TradeData(
                symbol=order.symbol,
                exchange=order.exchange,
                orderid=order.orderid,
                tradeid=order.orderid,
                direction=order.direction,
                offset=order.offset,
                price=trade_price,
                volume=order.volume,
                datetime=datetime.now(LOCAL_TZ),
                gateway_name=order.gateway_name
            )
            self.put_event(EVENT_TRADE, trade)

            self.update_position(trade, contract)

    def update_position(self, trade: TradeData, contract: ContractData):
        """"""
        vt_symbol = trade.vt_symbol

        # Net position mode
        if contract.net_position:
            position = self.get_position(vt_symbol, Direction.NET)

            if trade.direction == Direction.LONG:
                position.volume += trade.volume
            else:
                position.volume -= trade.volume

            self.put_event(EVENT_POSITION, copy(position))
        # Long/Short position mode
        else:
            long_position = self.get_position(vt_symbol, Direction.LONG)
            short_position = self.get_position(vt_symbol, Direction.SHORT)

            if trade.direction == Direction.LONG:
                if trade.offset == Offset.OPEN:
                    long_position.volume += trade.volume
                else:
                    short_position.volume -= trade.volume
                    short_position.frozen -= trade.volume
            else:
                if trade.offset == Offset.OPEN:
                    short_position.volume += trade.volume
                else:
                    long_position.volume -= trade.volume
                    long_position.frozen -= trade.volume

            self.put_event(EVENT_POSITION, long_position)
            self.put_event(EVENT_POSITION, short_position)

        self.save_data()

    def get_position(self, vt_symbol: str, direction: Direction):
        """"""
        key = (vt_symbol, direction)

        if key in self.positions:
            return self.positions[key]
        else:
            symbol, exchange = extract_vt_symbol(vt_symbol)
            position = PositionData(
                symbol=symbol,
                exchange=exchange,
                direction=direction,
                gateway_name=GATEWAY_NAME
            )

            self.positions[key] = position
            return position

    def write_log(self, msg: str) -> None:
        """"""
        log = LogData(msg=msg, gateway_name=GATEWAY_NAME)
        self.put_event(EVENT_LOG, log)

    def save_data(self) -> None:
        """"""
        position_data = []

        for position in self.positions.values():
            d = {
                "vt_symbol": position.vt_symbol,
                "volume": position.volume,
                "direction": position.direction.value
            }
            position_data.append(d)

        save_json(self.data_filename, position_data)

    def load_data(self) -> None:
        """"""
        position_data = load_json(self.data_filename)

        for d in position_data:
            vt_symbol = d["vt_symbol"]
            direction = Direction(d["direction"])

            position = self.get_position(vt_symbol, direction)
            position.volume = d["volume"]

    def clear_position(self) -> None:
        """"""
        for position in self.positions.values():
            position.volume = 0
            position.frozen = 0
            self.put_event(EVENT_POSITION, position)<|endoftext|>


<commit_before>from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.utils import formats
from django.utils import timezone
from . import log_changes
from .forms import EventEditForm, ArticleEditForm, UploadForm
from .models import Event, Article, Upload
from itertools import chain

def event(request, event_id):
    requested_event = get_object_or_404(Event, pk=event_id)
    context = {
        'event': requested_event,
    }

    return render(request, 'event.html', context)

def all_news(request):
    article_list = list(Article.objects.order_by('pub_date'))
    event_list = list(Event.objects.order_by('pub_date'))
    news_list = []
    # Create a list mixed with both articles and events sorted after publication date
    for i in range(len(article_list) + len(event_list)):
        if len(article_list) == 0:
            news_list.append(event_list.pop())
            continue
        elif len(event_list) == 0:
            news_list.append(article_list.pop())
            continue
        if article_list[len(article_list) - 1].pub_date > event_list[len(event_list) - 1].time_start:
            news_list.append(article_list.pop())
        else:
            news_list.append(event_list.pop())

    context = {
        'news_list': news_list,
    }

    return render(request, 'all_news.html', context)

def article(request, article_id):
    requested_article = get_object_or_404(Article, pk=article_id)
    context = {
        'article': requested_article,
    }

    return render(request, 'article.html', context)

def edit_event(request, event_id):
    if request.method == 'POST':
        form = EventEditForm(request.POST)
        if form.is_valid():
            event_id = form.cleaned_data['event_id']
            if event_id == 0:
                event = Event(time_start=timezone.now(), time_end=timezone.now())
            else:
                event = get_object_or_404(Event,pk=event_id)
            event.title = form.cleaned_data['title']
            event.ingress_content = form.cleaned_data['ingress_content']
            event.main_content = form.cleaned_data['main_content']
            event.thumbnail = form.cleaned_data['thumbnail']
            event.place = form.cleaned_data['place']
            event.place_href = form.cleaned_data['place_href']
            hour_start = int(form.cleaned_data['time_start'][:2])
            minute_start = int(form.cleaned_data['time_start'][-2:])
            hour_end = int(form.cleaned_data['time_end'][:2])
            minute_end = int(form.cleaned_data['time_end'][-2:])
            day = int(form.cleaned_data['date'][:2])
            month = int(form.cleaned_data['date'][3:5])
            year = int(form.cleaned_data['date'][-4:])
            event.time_start = event.time_start.replace(hour=hour_start, minute=minute_start)
            event.time_start = event.time_start.replace(day=day, month=month, year=year)
            event.time_end = event.time_end.replace(hour=hour_end, minute=minute_end)
            event.time_end = event.time_end.replace(day=day, month=month, year=year)
            event.save()
            log_changes.change(request, event)
            return HttpResponseRedirect('/news/event/' + str(event.id) + '/')
    else:
        if int(event_id) == 0:
            form = EventEditForm(initial={
                'event_id': 0,
                'time_start': '00:00',
                'time_end': '00:00',
                'date': formats.date_format(timezone.now(), 'd/m/Y'),
            })
        else:
            requested_event = Event.objects.get(pk=event_id)
            form = EventEditForm(initial={
                'title': requested_event.title,
                'event_id': event_id,
                'ingress_content': requested_event.ingress_content,
                'main_content': requested_event.main_content,
                'thumbnail': requested_event.thumbnail,
                'place': requested_event.place,
                'place_href': requested_event.place_href,
                'time_start': formats.date_format(requested_event.time_start, 'H:i'),
                'time_end': formats.date_format(requested_event.time_end, 'H:i'),
                'date': formats.date_format(requested_event.time_start, 'd/m/Y'),
            })

    context = {
        'form': form,
        'event_id': event_id,
    }

    return render(request, 'edit_event.html', context)

def edit_article(request, article_id):
    if request.method == 'POST':
        form = ArticleEditForm(request.POST)
        if form.is_valid():
            article_id = form.cleaned_data['article_id']
            if not article_id:
                article = Article()
            else:
                article = get_object_or_404(Article, pk=article_id)
            article.title = form.cleaned_data['title']
            article.ingress_content = form.cleaned_data['ingress_content']
            article.main_content = form.cleaned_data['main_content']
            article.thumbnail = form.cleaned_data['thumbnail']
            article.save()
            log_changes.change(request, article)

            return HttpResponseRedirect('/news/article/' + str(article.id) + '/')
    else:
        if int(article_id) == 0:
            form = ArticleEditForm(initial={
                'article_id': 0,
            })
        else:
            requested_article = Article.objects.get(pk=article_id)
            form = ArticleEditForm(initial={
                'title': requested_article.title,
                'article_id': article_id,
                'ingress_content': requested_article.ingress_content,
                'main_content': requested_article.main_content,
                'thumbnail': requested_article.thumbnail,
            })
    context = {
        'form': form,
        'article_id': article_id,
    }

    return render(request, 'edit_article.html', context)

def upload_file(request):
    if request.method == 'POST':
        form = UploadForm(request.POST, request.FILES)
        if form.is_valid():
            title = str(form.cleaned_data['title']).replace(" ", "_")
            file = request.FILES['file']
            number = 0

            for element in Upload.objects.order_by('-time'):
                if title == element.title:
                    number = element.number + 1
                    break

            ext = file.name.split(".")[-1:][0]
            file.name = "/upload/" + title + "_" + str(number) + "." + ext
            instance = Upload(file=file, title=title, time=timezone.now(), number=number)
            instance.save()
            return HttpResponseRedirect('/news/upload-done')
    else:
        form = UploadForm()

    context = {
        'form': form,
    }
    return render(request, 'upload.html', context)

def upload_done(request):
    return render(request, 'upload_done.html')
<commit_msg>Fix news sorting algorithm<commit_after>from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.utils import formats
from django.utils import timezone
from . import log_changes
from .forms import EventEditForm, ArticleEditForm, UploadForm
from .models import Event, Article, Upload
from itertools import chain

def event(request, event_id):
    requested_event = get_object_or_404(Event, pk=event_id)
    context = {
        'event': requested_event,
    }

    return render(request, 'event.html', context)

def all_news(request):
    article_list = list(Article.objects.order_by('pub_date'))
    event_list = list(Event.objects.order_by('time_start'))
    news_list = []
    # Create a list mixed with both articles and events sorted after publication date
    for i in range(len(article_list) + len(event_list)):
        if len(article_list) == 0:
            news_list.append(event_list.pop())
            continue
        elif len(event_list) == 0:
            news_list.append(article_list.pop())
            continue
        if article_list[len(article_list) - 1].pub_date > event_list[len(event_list) - 1].time_start:
            news_list.append(article_list.pop())
        else:
            news_list.append(event_list.pop())

    context = {
        'news_list': news_list,
    }

    return render(request, 'all_news.html', context)

def article(request, article_id):
    requested_article = get_object_or_404(Article, pk=article_id)
    context = {
        'article': requested_article,
    }

    return render(request, 'article.html', context)

def edit_event(request, event_id):
    if request.method == 'POST':
        form = EventEditForm(request.POST)
        if form.is_valid():
            event_id = form.cleaned_data['event_id']
            if event_id == 0:
                event = Event(time_start=timezone.now(), time_end=timezone.now())
            else:
                event = get_object_or_404(Event,pk=event_id)
            event.title = form.cleaned_data['title']
            event.ingress_content = form.cleaned_data['ingress_content']
            event.main_content = form.cleaned_data['main_content']
            event.thumbnail = form.cleaned_data['thumbnail']
            event.place = form.cleaned_data['place']
            event.place_href = form.cleaned_data['place_href']
            hour_start = int(form.cleaned_data['time_start'][:2])
            minute_start = int(form.cleaned_data['time_start'][-2:])
            hour_end = int(form.cleaned_data['time_end'][:2])
            minute_end = int(form.cleaned_data['time_end'][-2:])
            day = int(form.cleaned_data['date'][:2])
            month = int(form.cleaned_data['date'][3:5])
            year = int(form.cleaned_data['date'][-4:])
            event.time_start = event.time_start.replace(hour=hour_start, minute=minute_start)
            event.time_start = event.time_start.replace(day=day, month=month, year=year)
            event.time_end = event.time_end.replace(hour=hour_end, minute=minute_end)
            event.time_end = event.time_end.replace(day=day, month=month, year=year)
            event.save()
            log_changes.change(request, event)
            return HttpResponseRedirect('/news/event/' + str(event.id) + '/')
    else:
        if int(event_id) == 0:
            form = EventEditForm(initial={
                'event_id': 0,
                'time_start': '00:00',
                'time_end': '00:00',
                'date': formats.date_format(timezone.now(), 'd/m/Y'),
            })
        else:
            requested_event = Event.objects.get(pk=event_id)
            form = EventEditForm(initial={
                'title': requested_event.title,
                'event_id': event_id,
                'ingress_content': requested_event.ingress_content,
                'main_content': requested_event.main_content,
                'thumbnail': requested_event.thumbnail,
                'place': requested_event.place,
                'place_href': requested_event.place_href,
                'time_start': formats.date_format(requested_event.time_start, 'H:i'),
                'time_end': formats.date_format(requested_event.time_end, 'H:i'),
                'date': formats.date_format(requested_event.time_start, 'd/m/Y'),
            })

    context = {
        'form': form,
        'event_id': event_id,
    }

    return render(request, 'edit_event.html', context)

def edit_article(request, article_id):
    if request.method == 'POST':
        form = ArticleEditForm(request.POST)
        if form.is_valid():
            article_id = form.cleaned_data['article_id']
            if not article_id:
                article = Article()
            else:
                article = get_object_or_404(Article, pk=article_id)
            article.title = form.cleaned_data['title']
            article.ingress_content = form.cleaned_data['ingress_content']
            article.main_content = form.cleaned_data['main_content']
            article.thumbnail = form.cleaned_data['thumbnail']
            article.save()
            log_changes.change(request, article)

            return HttpResponseRedirect('/news/article/' + str(article.id) + '/')
    else:
        if int(article_id) == 0:
            form = ArticleEditForm(initial={
                'article_id': 0,
            })
        else:
            requested_article = Article.objects.get(pk=article_id)
            form = ArticleEditForm(initial={
                'title': requested_article.title,
                'article_id': article_id,
                'ingress_content': requested_article.ingress_content,
                'main_content': requested_article.main_content,
                'thumbnail': requested_article.thumbnail,
            })
    context = {
        'form': form,
        'article_id': article_id,
    }

    return render(request, 'edit_article.html', context)

def upload_file(request):
    if request.method == 'POST':
        form = UploadForm(request.POST, request.FILES)
        if form.is_valid():
            title = str(form.cleaned_data['title']).replace(" ", "_")
            file = request.FILES['file']
            number = 0

            for element in Upload.objects.order_by('-time'):
                if title == element.title:
                    number = element.number + 1
                    break

            ext = file.name.split(".")[-1:][0]
            file.name = "/upload/" + title + "_" + str(number) + "." + ext
            instance = Upload(file=file, title=title, time=timezone.now(), number=number)
            instance.save()
            return HttpResponseRedirect('/news/upload-done')
    else:
        form = UploadForm()

    context = {
        'form': form,
    }
    return render(request, 'upload.html', context)

def upload_done(request):
    return render(request, 'upload_done.html')
<|endoftext|>


<commit_before>from collections import deque
from os import path

from .utils import get_unique_id

class EndOfFile(Exception):
    pass

def tex_file_to_chars(file_name):
    end = path.extsep + 'tex'
    if not file_name.endswith(end):
        file_name += end
    with open(file_name, 'rb') as f:
        return [chr(b) for b in f.read()]

class ReaderBuffer(object):

    def __init__(self, file_name):
        self.i = -1
        self.chars = tex_file_to_chars(file_name)

        self.line_nr = 1
        self.col_nr = 1

    @property
    def at_last_char(self):
        return self.i == len(self.chars) - 1

    def peek_ahead(self, n):
        if n < 0:
            raise ValueError('Cannot peek backwards')
        if n > 3:
            raise ValueError('Peeking ahead so far is forbidden, as lies might'
                             'be returned')
        i_peek = self.i + n
        if i_peek < 0:
            raise ValueError('Cannot peek before beginning of buffer')
        try:
            return self.chars[i_peek]
        except IndexError:
            raise EndOfFile

    def increment_loc(self):
        self.i += 1
        c = self.peek_ahead(0)
        if c == '\n':
            self.line_nr += 1
            self.col_nr = 0
        else:
            self.col_nr += 1

class Reader(object):

    def __init__(self, file_name):
        # This implementation is a bit lazy: there's a big map of hashes to
        # reader buffers, and a stack to hold hashes representing the active
        # buffers. The true correct structure is an ordered tree, but I can't
        # be bothered doing this. This method keeps around old buffers for
        # debugging purposes.
        self.active_buffer_hash_stack = []
        self.buffer_map = {}
        self.insert_file(file_name)

    def make_new_buffer(self, file_name):
        # Add a hash in case of duplicated file names. I am being very
        # responsible.
        new_hash = (file_name, get_unique_id())
        self.buffer_map[new_hash] = ReaderBuffer(file_name)
        return new_hash

    def get_buffer(self, file_hash):
        return self.buffer_map[file_hash]

    def insert_file(self, file_name):
        self.active_buffer_hash_stack.append(self.make_new_buffer(file_name))

    @property
    def current_hash(self):
        return self.active_buffer_hash_stack[-1]

    @property
    def current_buffer(self):
        return self.buffer_map[self.current_hash]

    @property
    def current_chars(self):
        return self.current_buffer.chars

    @property
    def line_nr(self):
        return self.current_buffer.line_nr

    @property
    def col_nr(self):
        return self.current_buffer.col_nr

    @property
    def char_nr(self):
        return self.current_buffer.i

    @property
    def active_buffers_read_order(self):
        for buffer_hash in reversed(self.active_buffer_hash_stack):
            yield self.get_buffer(buffer_hash)

    def peek_ahead(self, n=1):
        if n < 0:
            raise ValueError('Cannot peek backwards')
        elif n > 3:
            raise ValueError('Peeking ahead so far is forbidden, as lies might'
                             'be returned')
        elif n == 0:
            return self.current_buffer.peek_ahead(0)
        n_to_do = n
        # Look at each buffer we are reading from, in reading order.
        for read_buffer in self.active_buffers_read_order:
            # Try to read successively further ahead, up to our aimed distance.
            # Don't try zero, because it might fail at the start of a buffer.
            for n_to_try in range(1, n_to_do + 1):
                try:
                    char = read_buffer.peek_ahead(n_to_try)
                except EndOfFile:
                    break
            # If we have read our aimed distance, then we have our peeked
            # character, and can return.
            else:
                return char
            # We can only get here by a `break` caused by an end-of-file.
            # Subtract as many units as we could read.
            # The last value of `n_to_try` is what failed, so we successfully
            # read one less than this.
            n_to_do -= n_to_try - 1
        raise EndOfFile

    def increment_loc(self):
        if self.current_buffer.at_last_char:
            self.active_buffer_hash_stack.pop()
        # If that was the last buffer, we are done.
        if not self.active_buffer_hash_stack:
            raise EndOfFile
        self.current_buffer.increment_loc()

    def advance_loc(self, n=1):
        if n > 2:
            raise ValueError('Advancing so far is forbidden')
        if n <= 0:
            raise ValueError('Cannot advance backwards or not at all')
        for _ in range(n):
            self.increment_loc()
        return self.peek_ahead(0)

    def advance_to_end(self):
        while True:
            try:
                yield self.advance_loc()
            except EndOfFile:
                break
<commit_msg>Add comments to reader<commit_after>from collections import deque
from os import path

from .utils import get_unique_id

class EndOfFile(Exception):
    pass

def tex_file_to_chars(file_name):
    end = path.extsep + 'tex'
    if not file_name.endswith(end):
        file_name += end
    with open(file_name, 'rb') as f:
        return [chr(b) for b in f.read()]

class ReaderBuffer(object):

    def __init__(self, file_name):
        self.i = -1
        self.chars = tex_file_to_chars(file_name)

        self.line_nr = 1
        self.col_nr = 1

    @property
    def at_last_char(self):
        return self.i == len(self.chars) - 1

    def peek_ahead(self, n):
        if n < 0:
            raise ValueError('Cannot peek backwards')
        if n > 3:
            raise ValueError('Peeking ahead so far is forbidden, as lies might'
                             'be returned')
        i_peek = self.i + n
        if i_peek < 0:
            raise ValueError('Cannot peek before beginning of buffer')
        try:
            return self.chars[i_peek]
        except IndexError:
            raise EndOfFile

    def increment_loc(self):
        self.i += 1
        c = self.peek_ahead(0)
        if c == '\n':
            self.line_nr += 1
            self.col_nr = 0
        else:
            self.col_nr += 1

class Reader(object):
    """Provides an abstract interface to a stack of buffers (at the moment
    always files). A new buffer can be added to the stack, from where
    characters will be read off until it is exhausted, at which point the
    previous buffer will be read from, and so on.
    """

    def __init__(self, file_name):
        # This implementation is a bit lazy: there's a big map of hashes to
        # reader buffers, and a stack to hold hashes representing the active
        # buffers. I think the true structure is an ordered tree, but I can't
        # be bothered doing this. This method keeps around old buffers for
        # debugging purposes.
        self.active_buffer_hash_stack = []
        self.buffer_map = {}
        self.insert_file(file_name)

    def make_new_buffer(self, file_name):
        """Make a new buffer, assign it a unique key, and return this so it can
        be accessed again later. The key is a combination of the file name and
        a unique hash, so that duplicate file names do not collide"""
        new_hash = (file_name, get_unique_id())
        self.buffer_map[new_hash] = ReaderBuffer(file_name)
        return new_hash

    def get_buffer(self, file_hash):
        """Access a buffer by its hash."""
        return self.buffer_map[file_hash]

    def insert_file(self, file_name):
        """Make a new buffer, and add it to the active stack."""
        self.active_buffer_hash_stack.append(self.make_new_buffer(file_name))

    @property
    def current_hash(self):
        """The hash of the buffer currently being read."""
        return self.active_buffer_hash_stack[-1]

    @property
    def current_buffer(self):
        """The buffer currently being read."""
        return self.buffer_map[self.current_hash]

    @property
    def current_chars(self):
        """The characters in the buffer currently being read."""
        return self.current_buffer.chars

    @property
    def line_nr(self):
        """The line number of the current character in the current
        buffer."""
        return self.current_buffer.line_nr

    @property
    def col_nr(self):
        """The column number of the current character in the current
        buffer."""
        return self.current_buffer.col_nr

    @property
    def char_nr(self):
        """The index of the current character in the current
        buffer, relative to the start of that buffer."""
        return self.current_buffer.i

    @property
    def active_buffers_read_order(self):
        """An iterator over the buffers either being read, or still to be fully
        read, in order from current to last to be read."""
        for buffer_hash in reversed(self.active_buffer_hash_stack):
            yield self.get_buffer(buffer_hash)

    def peek_ahead(self, n=1):
        """Get a character `n` places from the current position, without
        changing the reader's current position.
        `n < 0` is not possible, because read history is not kept.
        `n > 3` is not allowed, because peeking too far is dangerous: beyond
        a point, the characters' meanings might change the correct buffers to
        `n = 0` returns the current position.
        This function will peek between buffer boundaries if necessary."""
        if n < 0:
            raise ValueError('Cannot peek backwards')
        elif n > 3:
            raise ValueError('Peeking ahead so far is forbidden, as lies might'
                             'be returned')
        elif n == 0:
            return self.current_buffer.peek_ahead(0)
        n_to_do = n
        # Look at each buffer we are reading from, in reading order.
        for read_buffer in self.active_buffers_read_order:
            # Try to read successively further ahead, up to our aimed distance.
            # Don't try zero, because it might fail at the start of a buffer.
            for n_to_try in range(1, n_to_do + 1):
                try:
                    char = read_buffer.peek_ahead(n_to_try)
                except EndOfFile:
                    break
            # If we have read our aimed distance, then we have our peeked
            # character, and can return.
            else:
                return char
            # We can only get here by a `break` caused by an end-of-file.
            # Subtract as many units as we could read.
            # The last value of `n_to_try` is what failed, so we successfully
            # read one less than this.
            n_to_do -= n_to_try - 1
        raise EndOfFile

    def increment_loc(self):
        """Increment the reader's position, changing the current buffer if
        necessary."""
        if self.current_buffer.at_last_char:
            self.active_buffer_hash_stack.pop()
        # If that was the last buffer, we are done.
        if not self.active_buffer_hash_stack:
            raise EndOfFile
        self.current_buffer.increment_loc()

    def advance_loc(self, n=1):
        """Advance the reader's position by `n` places, changing the current
        buffer if necessary, and return the new current character, for
        convenience.
        `n < 0` is not possible, because read history is not kept.
        `n > 2` is not allowed, because advancing too far is dangerous: beyond
        a point, the characters' meanings might change the correct buffers to
        read from."""
        if n > 2:
            raise ValueError('Advancing so far is forbidden')
        if n < 0:
            raise ValueError('Cannot advance backwards')
        for _ in range(n):
            self.increment_loc()
        return self.peek_ahead(0)

    def advance_to_end(self):
        """Return an iterator over the reader's characters, until all buffers
        are exhausted. Use with care, as the characters' meanings, when acted
        on, may change the correct buffers that should be read."""
        while True:
            try:
                yield self.advance_loc()
            except EndOfFile:
                break
<|endoftext|>