<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2017, AGB & GC
# Full license can be found in License.md
#-----------------------------------------------------------------------------
""" Tests the ocb_scaling class and functions
"""

import ocbpy.instruments.general as ocb_igen
import unittest
import numpy as np

class TestGeneralMethods(unittest.TestCase):

    def setUp(self):
        """ Initialize the OCBoundary object using the test file, as well as
        the VectorData object
        """
        from os.path import isfile
        import ocbpy

        ocb_dir = ocbpy.__file__.split("/")
        self.test_file = "{:s}/{:s}".format("/".join(ocb_dir[:-1]),
                                            "tests/test_data/test_north_circle")
        self.assertTrue(isfile(self.test_file))

    def tearDown(self):
        del self.test_file

    def test_file_test(self):
        """ Test the general file testing routine
        """
        self.assertTrue(ocb_igen.test_file(self.test_file))
        self.assertFalse(ocb_igen.test_file("/"))
        print "Testing for warning above stating 'name provided is not a file'"

    def test_load_ascii_data(self):
        """ Test the general routine to load ASCII data
        """

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0, header=hh)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = sorted(hh[0].split())
        self.assertListEqual(ktest, sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":2000.0, "SOY":11187202.0, "NB":9.0, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

    def test_load_ascii_data_w_datetime(self):
        """ Test the general routine to load ASCII data
        """
        import datetime as dt

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0,
                                                datetime_cols=[0,1],
                                                datetime_fmt="YEAR SOY",
                                                header=hh)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = hh[0].split()
        ktest.append("datetime")
        self.assertListEqual(sorted(ktest), sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":2000, "SOY":11187202, "NB":9.0, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47,
                     "datetime":dt.datetime(2000,5,9,11,33,22)}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

if __name__ == '__main__':
    unittest.main()

<commit_msg>General data loading tests<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2017, AGB & GC
# Full license can be found in License.md
#-----------------------------------------------------------------------------
""" Tests the ocb_scaling class and functions
"""

import ocbpy.instruments.general as ocb_igen
import unittest
import numpy as np

class TestGeneralMethods(unittest.TestCase):

    def setUp(self):
        """ Initialize the OCBoundary object using the test file, as well as
        the VectorData object
        """
        from os.path import isfile
        import ocbpy

        ocb_dir = ocbpy.__file__.split("/")
        self.test_file = "{:s}/{:s}".format("/".join(ocb_dir[:-1]),
                                            "tests/test_data/test_north_circle")
        self.assertTrue(isfile(self.test_file))

    def tearDown(self):
        del self.test_file

    def test_file_test_true(self):
        """ Test the general file testing routine with a good file
        """
        self.assertTrue(ocb_igen.test_file(self.test_file))

    def test_file_test_false(self):
        """ Test the general file testing routine with a bad filename
        """
        self.assertFalse(ocb_igen.test_file("/"))
        print "Testing for warning above stating 'name provided is not a file'"

    def test_load_ascii_data_badfile(self):
        """ Test the general loading routine for ASCII data with bad input
        """
        header, data = ocb_igen.load_ascii_data("/", 0)
        self.assertIsInstance(header, list)
        self.assertEquals(len(header), 0)
        self.assertIsInstance(data, dict)
        self.assertEquals(len(data.keys()), 0)
        print "Testing for warning above stating 'name provided is not a file'"

    def test_load_ascii_data_standard(self):
        """ Test the general routine to load ASCII data
        """

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0, header=hh)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = sorted(hh[0].split())
        self.assertListEqual(ktest, sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":2000.0, "SOY":11187202.0, "NB":9.0, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

    def test_load_ascii_data_int_cols(self):
        """ Test the general routine to load ASCII data assigning some
        columns as integers
        """

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        int_cols = [0, 1, 2]
        int_keys = ["YEAR", "SOY", "NB"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0, header=hh,
                                                int_cols=int_cols)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = sorted(hh[0].split())
        self.assertListEqual(ktest, sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":2000, "SOY":11187202, "NB":9, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

            if kk in int_keys:
                self.assertIsInstance(data[kk][-1], int)
            else:
                self.assertIsInstance(data[kk][-1], float)

    def test_load_ascii_data_str_cols(self):
        """ Test the general routine to load ASCII data assigning some
        columns as strings
        """

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        str_cols = [0, 1]
        str_keys = ["YEAR", "SOY"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0, header=hh,
                                                str_cols=str_cols)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = sorted(hh[0].split())
        self.assertListEqual(ktest, sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":"2000", "SOY":"11187202", "NB":9, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

            if kk in str_keys:
                self.assertIsInstance(data[kk][-1], str)
            else:
                self.assertIsInstance(data[kk][-1], float)

    def test_load_ascii_data_w_datetime(self):
        """ Test the general routine to load ASCII data
        """
        import datetime as dt

        hh = ["YEAR SOY NB PHICENT RCENT R A RERR"]
        header, data = ocb_igen.load_ascii_data(self.test_file, 0,
                                                datetime_cols=[0,1],
                                                datetime_fmt="YEAR SOY",
                                                header=hh)

        # Test to ensure the output header equals the input header
        self.assertListEqual(header, hh)

        # Test to see that the data keys are all in the header
        ktest = hh[0].split()
        ktest.append("datetime")
        self.assertListEqual(sorted(ktest), sorted(data.keys()))

        # Test the length of the data file
        self.assertEqual(data['A'].shape[0], 75)

        # Test the values of the last data line
        test_vals = {"YEAR":2000, "SOY":11187202, "NB":9.0, "A":1.302e+07,
                     "PHICENT":315.29, "RCENT":2.67, "R":18.38, "RERR":0.47,
                     "datetime":dt.datetime(2000,5,9,11,33,22)}
        for kk in test_vals.keys():
            self.assertEqual(data[kk][-1], test_vals[kk])

if __name__ == '__main__':
    unittest.main()

<|endoftext|>


<commit_before># Copyright 2014-2016 The ODL development group
#
# This file is part of ODL.
#
# ODL is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ODL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ODL.  If not, see <http://www.gnu.org/licenses/>.

"""Partial objects for per-iterate actions in iterative methods."""

# Imports for common Python 2/3 codebase
from __future__ import print_function, division, absolute_import
from future import standard_library
standard_library.install_aliases()

from abc import ABCMeta, abstractmethod
import time

from odl.util.utility import with_metaclass

__all__ = ('Partial', 'StorePartial', 'ForEachPartial', 'PrintTimingPartial',
           'PrintIterationPartial', 'PrintNormPartial', 'ShowPartial')

class Partial(with_metaclass(ABCMeta, object)):

    """Abstract base class for sending partial results of iterations."""

    @abstractmethod
    def __call__(self, result):
        """Apply the partial object to result.

        Parameters
        ----------
        result : `LinearSpaceVector`
            Partial result after n iterations

        Returns
        -------
        `None`
        """

    def __and__(self, other):
        """Return ``self & other``

        Compose partials, calls both in sequence.

        Parameters
        ----------
        other : `Partial`
            The other partial to compose with

        Returns
        -------
        result : `Partial`
            A partial whose `__call__` method calls both constituents
            partials.

        Examples
        --------
        >>> store = StorePartial()
        >>> iter = PrintIterationPartial()
        >>> both = store & iter
        >>> both
        StorePartial() & PrintIterationPartial()
        """
        return AndPartial(self, other)

    def __repr__(self):
        """Return ``repr(self)``"""
        return '{}()'.format(self.__class__.__name__)

class AndPartial(Partial):

    """Partial used for combining several partials"""

    def __init__(self, *callbacks):
        """Initialize an instance.

        Parameters
        ----------
        *callbacks : `callable` or `Partial`'s
            Partials to be called in sequence as listed.
        """
        partials = [c if isinstance(c, Partial) else ForEachPartial(c)
                    for c in callbacks]

        self.partials = partials

    def __call__(self, result):
        """Apply all partials to result."""
        for p in self.partials:
            p(result)

    def __repr__(self):
        """Return ``repr(self)``"""
        return ' & '.join('{}'.format(p) for p in self.partials)

class StorePartial(Partial):

    """Simple object for storing all partial results of the solvers."""

    def __init__(self, results=None):
        """Initialize an instance.

        Parameters
        ----------
        results : `list`
            List in which to store the partial results.
            Default: new list (``[]``)
        """
        self._results = [] if results is None else results

    @property
    def results(self):
        """The partial results."""
        return self._results

    def __call__(self, result):
        """Append result to results list."""
        self._results.append(result.copy())

    def __iter__(self):
        """Allow iteration over the results"""
        return iter(self.results)

    def __getitem__(self, index):
        """Get partial result."""
        return self.results[index]

    def __len__(self):
        """The number of results stored."""
        return len(self.results)

    def __str__(self):
        """Return ``str(self)``"""
        resultstr = '' if self.results == [] else str(self.results)
        return 'StorePartial({})'.format(resultstr)

    def __repr__(self):
        """Return ``repr(self)``"""
        resultrepr = '' if self.results == [] else repr(self.results)
        return 'StorePartial({})'.format(resultrepr)

class ForEachPartial(Partial):

    """Simple object for applying a function to each iterate."""

    def __init__(self, function):
        """Initialize an instance.

        Parameters
        ----------
        function : `callable`
            Function to call for each iteration
        """
        assert callable(function)
        self.function = function

    def __call__(self, result):
        """Apply function to result."""
        self.function(result)

    def __str__(self):
        """Return ``str(self)``"""
        return 'ForEachPartial({})'.format(self.function)

    def __repr__(self):
        """Return ``repr(self)``"""
        return 'ForEachPartial({!r})'.format(self.function)

class PrintIterationPartial(Partial):

    """Print the iteration count."""

    _default_text = 'iter ='

    def __init__(self, text=None):
        """Initialize an instance.

        Parameters
        ----------
        text : `str`
            Text to display before the iteration count. Default: 'iter ='
        """
        self.text = text if text is not None else self._default_text
        self.iter = 0

    def __call__(self, _):
        """Print the current iteration."""
        print("{} {}".format(self.text, self.iter))
        self.iter += 1

    def __repr__(self):
        textstr = '' if self.text == self._default_text else self.text
        return 'PrintIterationPartial({})'.format(textstr)

class PrintTimingPartial(Partial):

    """Print the time elapsed since the previous iteration."""

    def __init__(self):
        """Initialize an instance."""
        self.time = time.time()

    def __call__(self, _):
        """Print time elapsed from the previous iteration."""
        t = time.time()
        print("Time elapsed = {:<5.03f} s".format(t - self.time))
        self.time = t

class PrintNormPartial(Partial):

    """Print the current norm."""

    def __init__(self):
        """Initialize an instance."""
        self.iter = 0

    def __call__(self, result):
        """Print the current norm."""
        print("norm = {}".format(result.norm()))
        self.iter += 1

class ShowPartial(Partial):

    """Show the partial result."""

    def __init__(self, **kwargs):
        """Initialize a new instance.

        Parameters are passed through to the vectors show method. Additional
        parameters included.

        Parameters
        ----------
        display_step : positive `int`, optional
            Number of iterations between plots. Default: 1

        Other Parameters
        ----------------
        kwargs :
            Optional arguments passed on to ``x.show``
        """
        self.kwargs = kwargs
        self.fig = kwargs.pop('fig', None)
        self.display_step = kwargs.pop('display_step', 1)
        self.iter = 0

    def __call__(self, x):
        """Show the current iterate."""
        if (self.iter % self.display_step) == 0:
            self.fig = x.show(fig=self.fig, **self.kwargs)

        self.iter += 1

    def __repr__(self):
        """Return ``repr(self)``"""
        return '{}(display_step={}, fig={}, **{!r})'.format(
            self.__class__.__name__,
            self.display_step,
            self.fig,
            self.kwargs)

if __name__ == '__main__':
    # pylint: disable=wrong-import-position
    from odl.util.testutils import run_doctests
    run_doctests()
<commit_msg>ENH: allow *args in ShowPartial<commit_after># Copyright 2014-2016 The ODL development group
#
# This file is part of ODL.
#
# ODL is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ODL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ODL.  If not, see <http://www.gnu.org/licenses/>.

"""Partial objects for per-iterate actions in iterative methods."""

# Imports for common Python 2/3 codebase
from __future__ import print_function, division, absolute_import
from future import standard_library
standard_library.install_aliases()

from abc import ABCMeta, abstractmethod
import time

from odl.util.utility import with_metaclass

__all__ = ('Partial', 'StorePartial', 'ForEachPartial', 'PrintTimingPartial',
           'PrintIterationPartial', 'PrintNormPartial', 'ShowPartial')

class Partial(with_metaclass(ABCMeta, object)):

    """Abstract base class for sending partial results of iterations."""

    @abstractmethod
    def __call__(self, result):
        """Apply the partial object to result.

        Parameters
        ----------
        result : `LinearSpaceVector`
            Partial result after n iterations

        Returns
        -------
        `None`
        """

    def __and__(self, other):
        """Return ``self & other``

        Compose partials, calls both in sequence.

        Parameters
        ----------
        other : `Partial`
            The other partial to compose with

        Returns
        -------
        result : `Partial`
            A partial whose `__call__` method calls both constituents
            partials.

        Examples
        --------
        >>> store = StorePartial()
        >>> iter = PrintIterationPartial()
        >>> both = store & iter
        >>> both
        StorePartial() & PrintIterationPartial()
        """
        return AndPartial(self, other)

    def __repr__(self):
        """Return ``repr(self)``"""
        return '{}()'.format(self.__class__.__name__)

class AndPartial(Partial):

    """Partial used for combining several partials"""

    def __init__(self, *callbacks):
        """Initialize an instance.

        Parameters
        ----------
        *callbacks : `callable` or `Partial`'s
            Partials to be called in sequence as listed.
        """
        partials = [c if isinstance(c, Partial) else ForEachPartial(c)
                    for c in callbacks]

        self.partials = partials

    def __call__(self, result):
        """Apply all partials to result."""
        for p in self.partials:
            p(result)

    def __repr__(self):
        """Return ``repr(self)``"""
        return ' & '.join('{}'.format(p) for p in self.partials)

class StorePartial(Partial):

    """Simple object for storing all partial results of the solvers."""

    def __init__(self, results=None):
        """Initialize an instance.

        Parameters
        ----------
        results : `list`
            List in which to store the partial results.
            Default: new list (``[]``)
        """
        self._results = [] if results is None else results

    @property
    def results(self):
        """The partial results."""
        return self._results

    def __call__(self, result):
        """Append result to results list."""
        self._results.append(result.copy())

    def __iter__(self):
        """Allow iteration over the results"""
        return iter(self.results)

    def __getitem__(self, index):
        """Get partial result."""
        return self.results[index]

    def __len__(self):
        """The number of results stored."""
        return len(self.results)

    def __str__(self):
        """Return ``str(self)``"""
        resultstr = '' if self.results == [] else str(self.results)
        return 'StorePartial({})'.format(resultstr)

    def __repr__(self):
        """Return ``repr(self)``"""
        resultrepr = '' if self.results == [] else repr(self.results)
        return 'StorePartial({})'.format(resultrepr)

class ForEachPartial(Partial):

    """Simple object for applying a function to each iterate."""

    def __init__(self, function):
        """Initialize an instance.

        Parameters
        ----------
        function : `callable`
            Function to call for each iteration
        """
        assert callable(function)
        self.function = function

    def __call__(self, result):
        """Apply function to result."""
        self.function(result)

    def __str__(self):
        """Return ``str(self)``"""
        return 'ForEachPartial({})'.format(self.function)

    def __repr__(self):
        """Return ``repr(self)``"""
        return 'ForEachPartial({!r})'.format(self.function)

class PrintIterationPartial(Partial):

    """Print the iteration count."""

    _default_text = 'iter ='

    def __init__(self, text=None):
        """Initialize an instance.

        Parameters
        ----------
        text : `str`
            Text to display before the iteration count. Default: 'iter ='
        """
        self.text = text if text is not None else self._default_text
        self.iter = 0

    def __call__(self, _):
        """Print the current iteration."""
        print("{} {}".format(self.text, self.iter))
        self.iter += 1

    def __repr__(self):
        textstr = '' if self.text == self._default_text else self.text
        return 'PrintIterationPartial({})'.format(textstr)

class PrintTimingPartial(Partial):

    """Print the time elapsed since the previous iteration."""

    def __init__(self):
        """Initialize an instance."""
        self.time = time.time()

    def __call__(self, _):
        """Print time elapsed from the previous iteration."""
        t = time.time()
        print("Time elapsed = {:<5.03f} s".format(t - self.time))
        self.time = t

class PrintNormPartial(Partial):

    """Print the current norm."""

    def __init__(self):
        """Initialize an instance."""
        self.iter = 0

    def __call__(self, result):
        """Print the current norm."""
        print("norm = {}".format(result.norm()))
        self.iter += 1

class ShowPartial(Partial):

    """Show the partial result."""

    def __init__(self, *args, **kwargs):
        """Initialize a new instance.

        Parameters are passed through to the vectors show method. Additional
        parameters included.

        Parameters
        ----------
        display_step : positive `int`, optional
            Number of iterations between plots. Default: 1

        Other Parameters
        ----------------
        kwargs :
            Optional arguments passed on to ``x.show``
        """
        self.args = args
        self.kwargs = kwargs
        self.fig = kwargs.pop('fig', None)
        self.display_step = kwargs.pop('display_step', 1)
        self.iter = 0

    def __call__(self, x):
        """Show the current iterate."""
        if (self.iter % self.display_step) == 0:
            self.fig = x.show(*self.args, fig=self.fig, **self.kwargs)

        self.iter += 1

    def __repr__(self):
        """Return ``repr(self)``"""

        return '{}(display_step={}, fig={}, *{!r}, **{!r})'.format(
            self.__class__.__name__,
            self.display_step,
            self.fig,
            self.args,
            self.kwargs)

if __name__ == '__main__':
    # pylint: disable=wrong-import-position
    from odl.util.testutils import run_doctests
    run_doctests()
<|endoftext|>


<commit_before># This file is part of Androguard.
#
# Copyright (c) 2012 Geoffroy Gueguen <geoffroy.gueguen@gmail.com>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
from collections import defaultdict
from androguard.decompiler.dad.basic_blocks import (
    CatchBlock, Condition, LoopBlock, ShortCircuitBlock, TryBlock)
from androguard.decompiler.dad.graph import Graph
from androguard.decompiler.dad.node import Interval
from androguard.decompiler.dad.util import common_dom

logger = logging.getLogger('dad.control_flow')

def intervals(graph):
    """
    Compute the intervals of the graph
    Returns
        interval_graph: a graph of the intervals of G
        interv_heads: a dict of (header node, interval)
    """
    interval_graph = Graph()  # graph of intervals
    heads = [graph.entry]  # list of header nodes
    interv_heads = {}  # interv_heads[i] = interval of header i
    processed = dict([(i, False) for i in graph])
    edges = defaultdict(list)

    while heads:
        head = heads.pop(0)

        if not processed[head]:
            processed[head] = True
            interv_heads[head] = Interval(head)

            # Check if there is a node which has all its predecessor in the
            # current interval. If there is, add that node to the interval and
            # repeat until all the possible nodes have been added.
            change = True
            while change:
                change = False
                for node in graph.rpo[1:]:
                    if all(
                                    p in interv_heads[head] for p in graph.all_preds(node)):
                        change |= interv_heads[head].add_node(node)

            # At this stage, a node which is not in the interval, but has one
            # of its predecessor in it, is the header of another interval. So
            # we add all such nodes to the header list.
            for node in graph:
                if node not in interv_heads[head] and node not in heads:
                    if any(
                                    p in interv_heads[head] for p in graph.all_preds(node)):
                        edges[interv_heads[head]].append(node)
                        assert (node not in heads)
                        heads.append(node)

            interval_graph.add_node(interv_heads[head])
            interv_heads[head].compute_end(graph)

    # Edges is a mapping of 'Interval -> [header nodes of interval successors]'
    for interval, heads in edges.items():
        for head in heads:
            interval_graph.add_edge(interval, interv_heads[head])

    interval_graph.entry = graph.entry.interval
    if graph.exit:
        interval_graph.exit = graph.exit.interval

    return interval_graph, interv_heads

def derived_sequence(graph):
    """
    Compute the derived sequence of the graph G
    The intervals of G are collapsed into nodes, intervals of these nodes are
    built, and the process is repeated iteratively until we obtain a single
    node (if the graph is not irreducible)
    """
    deriv_seq = [graph]
    deriv_interv = []
    single_node = False

    while not single_node:

        interv_graph, interv_heads = intervals(graph)
        deriv_interv.append(interv_heads)

        single_node = len(interv_graph) == 1
        if not single_node:
            deriv_seq.append(interv_graph)

        graph = interv_graph
        graph.compute_rpo()

    return deriv_seq, deriv_interv

def mark_loop_rec(graph, node, s_num, e_num, interval, nodes_in_loop):
    if node in nodes_in_loop:
        return
    nodes_in_loop.append(node)
    for pred in graph.all_preds(node):
        if s_num < pred.num <= e_num and pred in interval:
            mark_loop_rec(graph, pred, s_num, e_num, interval, nodes_in_loop)

def mark_loop(graph, start, end, interval):
    logger.debug('MARKLOOP : %s END : %s', start, end)
    head = start.get_head()
    latch = end.get_end()
    nodes_in_loop = [head]
    mark_loop_rec(graph, latch, head.num, latch.num, interval, nodes_in_loop)
    head.startloop = True
    head.latch = latch
    return nodes_in_loop

def loop_type(start, end, nodes_in_loop):
    if end.type.is_cond:
        if start.type.is_cond:
            if start.true in nodes_in_loop and start.false in nodes_in_loop:
                start.looptype.is_posttest = True
            else:
                start.looptype.is_pretest = True
        else:
            start.looptype.is_posttest = True
    else:
        if start.type.is_cond:
            if start.true in nodes_in_loop and start.false in nodes_in_loop:
                start.looptype.is_endless = True
            else:
                start.looptype.is_pretest = True
        else:
            start.looptype.is_endless = True

def loop_follow(start, end, nodes_in_loop):
    follow = None
    if start.looptype.is_pretest:
        if start.true in nodes_in_loop:
            follow = start.false
        else:
            follow = start.true
    elif start.looptype.is_posttest:
        if end.true in nodes_in_loop:
            follow = end.false
        else:
            follow = end.true
    else:
        num_next = float('inf')
        for node in nodes_in_loop:
            if node.type.is_cond:
                if (node.true.num < num_next and
                            node.true not in nodes_in_loop):
                    follow = node.true
                    num_next = follow.num
                elif (node.false.num < num_next and
                              node.false not in nodes_in_loop):
                    follow = node.false
                    num_next = follow.num
    start.follow['loop'] = follow
    for node in nodes_in_loop:
        node.follow['loop'] = follow
    logger.debug('Start of loop %s', start)
    logger.debug('Follow of loop: %s', start.follow['loop'])

def loop_struct(graphs_list, intervals_list):
    first_graph = graphs_list[0]
    for i, graph in enumerate(graphs_list):
        interval = intervals_list[i]
        for head in sorted(list(interval.keys()), key=lambda x: x.num):
            loop_nodes = []
            for node in graph.all_preds(head):
                if node.interval is head.interval:
                    lnodes = mark_loop(first_graph, head, node, head.interval)
                    for lnode in lnodes:
                        if lnode not in loop_nodes:
                            loop_nodes.append(lnode)
            head.get_head().loop_nodes = loop_nodes

def if_struct(graph, idoms):
    unresolved = set()
    for node in graph.post_order():
        if node.type.is_cond:
            ldominates = []
            for n, idom in idoms.items():
                if node is idom and len(graph.reverse_edges.get(n, [])) > 1:
                    ldominates.append(n)
            if len(ldominates) > 0:
                n = max(ldominates, key=lambda x: x.num)
                node.follow['if'] = n
                for x in unresolved.copy():
                    if node.num < x.num < n.num:
                        x.follow['if'] = n
                        unresolved.remove(x)
            else:
                unresolved.add(node)
    return unresolved

def switch_struct(graph, idoms):
    unresolved = set()
    for node in graph.post_order():
        if node.type.is_switch:
            m = node
            for suc in graph.sucs(node):
                if idoms[suc] is not node:
                    m = common_dom(idoms, node, suc)
            ldominates = []
            for n, dom in idoms.items():
                if m is dom and len(graph.all_preds(n)) > 1:
                    ldominates.append(n)
            if len(ldominates) > 0:
                n = max(ldominates, key=lambda x: x.num)
                node.follow['switch'] = n
                for x in unresolved:
                    x.follow['switch'] = n
                unresolved = set()
            else:
                unresolved.add(node)
            node.order_cases()

# TODO: deal with preds which are in catch
def short_circuit_struct(graph, idom, node_map):
    def MergeNodes(node1, node2, is_and, is_not):
        lpreds = set()
        ldests = set()
        for node in (node1, node2):
            lpreds.update(graph.preds(node))
            ldests.update(graph.sucs(node))
            graph.remove_node(node)
            done.add(node)
        lpreds.difference_update((node1, node2))
        ldests.difference_update((node1, node2))

        entry = graph.entry in (node1, node2)

        new_name = '%s+%s' % (node1.name, node2.name)
        condition = Condition(node1, node2, is_and, is_not)

        new_node = ShortCircuitBlock(new_name, condition)
        for old_n, new_n in node_map.items():
            if new_n in (node1, node2):
                node_map[old_n] = new_node
        node_map[node1] = new_node
        node_map[node2] = new_node
        idom[new_node] = idom[node1]
        idom.pop(node1)
        idom.pop(node2)
        new_node.copy_from(node1)

        graph.add_node(new_node)

        for pred in lpreds:
            pred.update_attribute_with(node_map)
            graph.add_edge(node_map.get(pred, pred), new_node)
        for dest in ldests:
            graph.add_edge(new_node, node_map.get(dest, dest))
        if entry:
            graph.entry = new_node
        return new_node

    change = True
    while change:
        change = False
        done = set()
        for node in graph.post_order():
            if node.type.is_cond and node not in done:
                then = node.true
                els = node.false
                if node in (then, els):
                    continue
                if then.type.is_cond and len(graph.preds(then)) == 1:
                    if node in (then.true, then.false):
                        continue
                    if then.false is els:  # node && t
                        change = True
                        merged_node = MergeNodes(node, then, True, False)
                        merged_node.true = then.true
                        merged_node.false = els
                    elif then.true is els:  # !node || t
                        change = True
                        merged_node = MergeNodes(node, then, False, True)
                        merged_node.true = els
                        merged_node.false = then.false
                elif els.type.is_cond and len(graph.preds(els)) == 1:
                    if node in (els.false, els.true):
                        continue
                    if els.false is then:  # !node && e
                        change = True
                        merged_node = MergeNodes(node, els, True, True)
                        merged_node.true = els.true
                        merged_node.false = then
                    elif els.true is then:  # node || e
                        change = True
                        merged_node = MergeNodes(node, els, False, False)
                        merged_node.true = then
                        merged_node.false = els.false
            done.add(node)
        if change:
            graph.compute_rpo()

def while_block_struct(graph, node_map):
    change = False
    for node in graph.rpo[:]:
        if node.startloop:
            change = True
            new_node = LoopBlock(node.name, node)
            node_map[node] = new_node
            new_node.copy_from(node)

            entry = node is graph.entry
            lpreds = graph.preds(node)
            lsuccs = graph.sucs(node)

            for pred in lpreds:
                graph.add_edge(node_map.get(pred, pred), new_node)

            for suc in lsuccs:
                graph.add_edge(new_node, node_map.get(suc, suc))
            if entry:
                graph.entry = new_node

            if node.type.is_cond:
                new_node.true = node.true
                new_node.false = node.false

            graph.add_node(new_node)
            graph.remove_node(node)

    if change:
        graph.compute_rpo()

def catch_struct(graph, idoms):
    block_try_nodes = {}
    node_map = {}
    for catch_block in graph.reverse_catch_edges:
        if catch_block in graph.catch_edges:
            continue
        catch_node = CatchBlock(catch_block)

        try_block = idoms[catch_block]
        try_node = block_try_nodes.get(try_block)
        if try_node is None:
            block_try_nodes[try_block] = TryBlock(try_block)
            try_node = block_try_nodes[try_block]

            node_map[try_block] = try_node
            for pred in graph.all_preds(try_block):
                pred.update_attribute_with(node_map)
                if try_block in graph.sucs(pred):
                    graph.edges[pred].remove(try_block)
                graph.add_edge(pred, try_node)

            if try_block.type.is_stmt:
                follow = graph.sucs(try_block)
                if follow:
                    try_node.follow = graph.sucs(try_block)[0]
                else:
                    try_node.follow = None
            elif try_block.type.is_cond:
                loop_follow = try_block.follow['loop']
                if loop_follow:
                    try_node.follow = loop_follow
                else:
                    try_node.follow = try_block.follow['if']
            elif try_block.type.is_switch:
                try_node.follow = try_block.follow['switch']
            else:  # return or throw
                try_node.follow = None

        try_node.add_catch_node(catch_node)
    for node in graph.nodes:
        node.update_attribute_with(node_map)
    if graph.entry in node_map:
        graph.entry = node_map[graph.entry]

def update_dom(idoms, node_map):
    for n, dom in idoms.items():
        idoms[n] = node_map.get(dom, dom)

def identify_structures(graph, idoms):
    Gi, Li = derived_sequence(graph)
    switch_struct(graph, idoms)
    loop_struct(Gi, Li)
    node_map = {}

    short_circuit_struct(graph, idoms, node_map)
    update_dom(idoms, node_map)

    if_unresolved = if_struct(graph, idoms)

    while_block_struct(graph, node_map)
    update_dom(idoms, node_map)

    loop_starts = []
    for node in graph.rpo:
        node.update_attribute_with(node_map)
        if node.startloop:
            loop_starts.append(node)
    for node in loop_starts:
        loop_type(node, node.latch, node.loop_nodes)
        loop_follow(node, node.latch, node.loop_nodes)

    for node in if_unresolved:
        follows = [n for n in (node.follow['loop'], node.follow['switch']) if n]
        if len(follows) >= 1:
            follow = min(follows, key=lambda x: x.num)
            node.follow['if'] = follow

    catch_struct(graph, idoms)
<commit_msg>unexpected indentation<commit_after># This file is part of Androguard.
#
# Copyright (c) 2012 Geoffroy Gueguen <geoffroy.gueguen@gmail.com>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
from collections import defaultdict
from androguard.decompiler.dad.basic_blocks import (
    CatchBlock, Condition, LoopBlock, ShortCircuitBlock, TryBlock)
from androguard.decompiler.dad.graph import Graph
from androguard.decompiler.dad.node import Interval
from androguard.decompiler.dad.util import common_dom

logger = logging.getLogger('dad.control_flow')

def intervals(graph):
    """
    Compute the intervals of the graph
    Returns
    interval_graph: a graph of the intervals of G
    interv_heads: a dict of (header node, interval)
    """
    interval_graph = Graph()  # graph of intervals
    heads = [graph.entry]  # list of header nodes
    interv_heads = {}  # interv_heads[i] = interval of header i
    processed = dict([(i, False) for i in graph])
    edges = defaultdict(list)

    while heads:
        head = heads.pop(0)

        if not processed[head]:
            processed[head] = True
            interv_heads[head] = Interval(head)

            # Check if there is a node which has all its predecessor in the
            # current interval. If there is, add that node to the interval and
            # repeat until all the possible nodes have been added.
            change = True
            while change:
                change = False
                for node in graph.rpo[1:]:
                    if all(
                                    p in interv_heads[head] for p in graph.all_preds(node)):
                        change |= interv_heads[head].add_node(node)

            # At this stage, a node which is not in the interval, but has one
            # of its predecessor in it, is the header of another interval. So
            # we add all such nodes to the header list.
            for node in graph:
                if node not in interv_heads[head] and node not in heads:
                    if any(
                                    p in interv_heads[head] for p in graph.all_preds(node)):
                        edges[interv_heads[head]].append(node)
                        assert (node not in heads)
                        heads.append(node)

            interval_graph.add_node(interv_heads[head])
            interv_heads[head].compute_end(graph)

    # Edges is a mapping of 'Interval -> [header nodes of interval successors]'
    for interval, heads in edges.items():
        for head in heads:
            interval_graph.add_edge(interval, interv_heads[head])

    interval_graph.entry = graph.entry.interval
    if graph.exit:
        interval_graph.exit = graph.exit.interval

    return interval_graph, interv_heads

def derived_sequence(graph):
    """
    Compute the derived sequence of the graph G
    The intervals of G are collapsed into nodes, intervals of these nodes are
    built, and the process is repeated iteratively until we obtain a single
    node (if the graph is not irreducible)
    """
    deriv_seq = [graph]
    deriv_interv = []
    single_node = False

    while not single_node:

        interv_graph, interv_heads = intervals(graph)
        deriv_interv.append(interv_heads)

        single_node = len(interv_graph) == 1
        if not single_node:
            deriv_seq.append(interv_graph)

        graph = interv_graph
        graph.compute_rpo()

    return deriv_seq, deriv_interv

def mark_loop_rec(graph, node, s_num, e_num, interval, nodes_in_loop):
    if node in nodes_in_loop:
        return
    nodes_in_loop.append(node)
    for pred in graph.all_preds(node):
        if s_num < pred.num <= e_num and pred in interval:
            mark_loop_rec(graph, pred, s_num, e_num, interval, nodes_in_loop)

def mark_loop(graph, start, end, interval):
    logger.debug('MARKLOOP : %s END : %s', start, end)
    head = start.get_head()
    latch = end.get_end()
    nodes_in_loop = [head]
    mark_loop_rec(graph, latch, head.num, latch.num, interval, nodes_in_loop)
    head.startloop = True
    head.latch = latch
    return nodes_in_loop

def loop_type(start, end, nodes_in_loop):
    if end.type.is_cond:
        if start.type.is_cond:
            if start.true in nodes_in_loop and start.false in nodes_in_loop:
                start.looptype.is_posttest = True
            else:
                start.looptype.is_pretest = True
        else:
            start.looptype.is_posttest = True
    else:
        if start.type.is_cond:
            if start.true in nodes_in_loop and start.false in nodes_in_loop:
                start.looptype.is_endless = True
            else:
                start.looptype.is_pretest = True
        else:
            start.looptype.is_endless = True

def loop_follow(start, end, nodes_in_loop):
    follow = None
    if start.looptype.is_pretest:
        if start.true in nodes_in_loop:
            follow = start.false
        else:
            follow = start.true
    elif start.looptype.is_posttest:
        if end.true in nodes_in_loop:
            follow = end.false
        else:
            follow = end.true
    else:
        num_next = float('inf')
        for node in nodes_in_loop:
            if node.type.is_cond:
                if (node.true.num < num_next and
                            node.true not in nodes_in_loop):
                    follow = node.true
                    num_next = follow.num
                elif (node.false.num < num_next and
                              node.false not in nodes_in_loop):
                    follow = node.false
                    num_next = follow.num
    start.follow['loop'] = follow
    for node in nodes_in_loop:
        node.follow['loop'] = follow
    logger.debug('Start of loop %s', start)
    logger.debug('Follow of loop: %s', start.follow['loop'])

def loop_struct(graphs_list, intervals_list):
    first_graph = graphs_list[0]
    for i, graph in enumerate(graphs_list):
        interval = intervals_list[i]
        for head in sorted(list(interval.keys()), key=lambda x: x.num):
            loop_nodes = []
            for node in graph.all_preds(head):
                if node.interval is head.interval:
                    lnodes = mark_loop(first_graph, head, node, head.interval)
                    for lnode in lnodes:
                        if lnode not in loop_nodes:
                            loop_nodes.append(lnode)
            head.get_head().loop_nodes = loop_nodes

def if_struct(graph, idoms):
    unresolved = set()
    for node in graph.post_order():
        if node.type.is_cond:
            ldominates = []
            for n, idom in idoms.items():
                if node is idom and len(graph.reverse_edges.get(n, [])) > 1:
                    ldominates.append(n)
            if len(ldominates) > 0:
                n = max(ldominates, key=lambda x: x.num)
                node.follow['if'] = n
                for x in unresolved.copy():
                    if node.num < x.num < n.num:
                        x.follow['if'] = n
                        unresolved.remove(x)
            else:
                unresolved.add(node)
    return unresolved

def switch_struct(graph, idoms):
    unresolved = set()
    for node in graph.post_order():
        if node.type.is_switch:
            m = node
            for suc in graph.sucs(node):
                if idoms[suc] is not node:
                    m = common_dom(idoms, node, suc)
            ldominates = []
            for n, dom in idoms.items():
                if m is dom and len(graph.all_preds(n)) > 1:
                    ldominates.append(n)
            if len(ldominates) > 0:
                n = max(ldominates, key=lambda x: x.num)
                node.follow['switch'] = n
                for x in unresolved:
                    x.follow['switch'] = n
                unresolved = set()
            else:
                unresolved.add(node)
            node.order_cases()

# TODO: deal with preds which are in catch
def short_circuit_struct(graph, idom, node_map):
    def MergeNodes(node1, node2, is_and, is_not):
        lpreds = set()
        ldests = set()
        for node in (node1, node2):
            lpreds.update(graph.preds(node))
            ldests.update(graph.sucs(node))
            graph.remove_node(node)
            done.add(node)
        lpreds.difference_update((node1, node2))
        ldests.difference_update((node1, node2))

        entry = graph.entry in (node1, node2)

        new_name = '%s+%s' % (node1.name, node2.name)
        condition = Condition(node1, node2, is_and, is_not)

        new_node = ShortCircuitBlock(new_name, condition)
        for old_n, new_n in node_map.items():
            if new_n in (node1, node2):
                node_map[old_n] = new_node
        node_map[node1] = new_node
        node_map[node2] = new_node
        idom[new_node] = idom[node1]
        idom.pop(node1)
        idom.pop(node2)
        new_node.copy_from(node1)

        graph.add_node(new_node)

        for pred in lpreds:
            pred.update_attribute_with(node_map)
            graph.add_edge(node_map.get(pred, pred), new_node)
        for dest in ldests:
            graph.add_edge(new_node, node_map.get(dest, dest))
        if entry:
            graph.entry = new_node
        return new_node

    change = True
    while change:
        change = False
        done = set()
        for node in graph.post_order():
            if node.type.is_cond and node not in done:
                then = node.true
                els = node.false
                if node in (then, els):
                    continue
                if then.type.is_cond and len(graph.preds(then)) == 1:
                    if node in (then.true, then.false):
                        continue
                    if then.false is els:  # node && t
                        change = True
                        merged_node = MergeNodes(node, then, True, False)
                        merged_node.true = then.true
                        merged_node.false = els
                    elif then.true is els:  # !node || t
                        change = True
                        merged_node = MergeNodes(node, then, False, True)
                        merged_node.true = els
                        merged_node.false = then.false
                elif els.type.is_cond and len(graph.preds(els)) == 1:
                    if node in (els.false, els.true):
                        continue
                    if els.false is then:  # !node && e
                        change = True
                        merged_node = MergeNodes(node, els, True, True)
                        merged_node.true = els.true
                        merged_node.false = then
                    elif els.true is then:  # node || e
                        change = True
                        merged_node = MergeNodes(node, els, False, False)
                        merged_node.true = then
                        merged_node.false = els.false
            done.add(node)
        if change:
            graph.compute_rpo()

def while_block_struct(graph, node_map):
    change = False
    for node in graph.rpo[:]:
        if node.startloop:
            change = True
            new_node = LoopBlock(node.name, node)
            node_map[node] = new_node
            new_node.copy_from(node)

            entry = node is graph.entry
            lpreds = graph.preds(node)
            lsuccs = graph.sucs(node)

            for pred in lpreds:
                graph.add_edge(node_map.get(pred, pred), new_node)

            for suc in lsuccs:
                graph.add_edge(new_node, node_map.get(suc, suc))
            if entry:
                graph.entry = new_node

            if node.type.is_cond:
                new_node.true = node.true
                new_node.false = node.false

            graph.add_node(new_node)
            graph.remove_node(node)

    if change:
        graph.compute_rpo()

def catch_struct(graph, idoms):
    block_try_nodes = {}
    node_map = {}
    for catch_block in graph.reverse_catch_edges:
        if catch_block in graph.catch_edges:
            continue
        catch_node = CatchBlock(catch_block)

        try_block = idoms[catch_block]
        try_node = block_try_nodes.get(try_block)
        if try_node is None:
            block_try_nodes[try_block] = TryBlock(try_block)
            try_node = block_try_nodes[try_block]

            node_map[try_block] = try_node
            for pred in graph.all_preds(try_block):
                pred.update_attribute_with(node_map)
                if try_block in graph.sucs(pred):
                    graph.edges[pred].remove(try_block)
                graph.add_edge(pred, try_node)

            if try_block.type.is_stmt:
                follow = graph.sucs(try_block)
                if follow:
                    try_node.follow = graph.sucs(try_block)[0]
                else:
                    try_node.follow = None
            elif try_block.type.is_cond:
                loop_follow = try_block.follow['loop']
                if loop_follow:
                    try_node.follow = loop_follow
                else:
                    try_node.follow = try_block.follow['if']
            elif try_block.type.is_switch:
                try_node.follow = try_block.follow['switch']
            else:  # return or throw
                try_node.follow = None

        try_node.add_catch_node(catch_node)
    for node in graph.nodes:
        node.update_attribute_with(node_map)
    if graph.entry in node_map:
        graph.entry = node_map[graph.entry]

def update_dom(idoms, node_map):
    for n, dom in idoms.items():
        idoms[n] = node_map.get(dom, dom)

def identify_structures(graph, idoms):
    Gi, Li = derived_sequence(graph)
    switch_struct(graph, idoms)
    loop_struct(Gi, Li)
    node_map = {}

    short_circuit_struct(graph, idoms, node_map)
    update_dom(idoms, node_map)

    if_unresolved = if_struct(graph, idoms)

    while_block_struct(graph, node_map)
    update_dom(idoms, node_map)

    loop_starts = []
    for node in graph.rpo:
        node.update_attribute_with(node_map)
        if node.startloop:
            loop_starts.append(node)
    for node in loop_starts:
        loop_type(node, node.latch, node.loop_nodes)
        loop_follow(node, node.latch, node.loop_nodes)

    for node in if_unresolved:
        follows = [n for n in (node.follow['loop'], node.follow['switch']) if n]
        if len(follows) >= 1:
            follow = min(follows, key=lambda x: x.num)
            node.follow['if'] = follow

    catch_struct(graph, idoms)
<|endoftext|>


<commit_before>import os
import sys
from django.contrib.messages import constants as messages

# Directory that contains this file.
PROJECT_SETTINGS_DIRECTORY = os.path.dirname(globals()['__file__'])
# Root directory. Contains manage.py
PROJECT_ROOT_DIRECTORY = os.path.join(PROJECT_SETTINGS_DIRECTORY, '..', '..')

TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
NOSE_ARGS = ['--with-coverage', '--cover-package=apps']

DEBUG = False
TEMPLATE_DEBUG = DEBUG

TEST_RUNNER = "django_nose.NoseTestSuiteRunner"

ADMINS = (
    ('dotKom', 'dotkom@online.ntnu.no'),
)
MANAGERS = ADMINS

# Email settings
DEFAULT_FROM_EMAIL = 'online@online.ntnu.no'
EMAIL_ARRKOM = 'arrkom@online.ntnu.no'
EMAIL_BEDKOM = 'bedkom@online.ntnu.no'
EMAIL_DOTKOM = 'dotkom@online.ntnu.no'
EMAIL_FAGKOM = 'fagkom@online.ntnu.no'
EMAIL_PROKOM = 'prokom@online.ntnu.no'
EMAIL_TRIKOM = 'trikom@online.ntnu.no'

# We will receive errors and other django messages from this email
SERVER_EMAIL = 'onlineweb4-error@online.ntnu.no'

TIME_ZONE = 'Europe/Oslo'

# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'nb'
LANGUAGES = (
                ('nb', 'Norwegian'),
                ('en_US', 'English'),
            )
LOCALE_PATHS = [
    os.path.join(PROJECT_ROOT_DIRECTORY, 'locale'),
]

SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
DATETIME_FORMAT = 'N j, Y, H:i'
SECRET_KEY = 'override-this-in-local.py'

# Session cookie expires after one year
SESSION_COOKIE_AGE = 31540000

# Override this in local if you need to :)
BASE_URL = 'https://online.ntnu.no'

AUTH_USER_MODEL = 'authentication.OnlineUser'
LOGIN_URL = '/auth/login/'

MEDIA_ROOT = os.path.join(PROJECT_ROOT_DIRECTORY, 'uploaded_media') # Override this in local.py in prod.
MEDIA_URL = '/media/'

STATIC_ROOT = os.path.join(PROJECT_ROOT_DIRECTORY, 'static')
STATIC_URL = '/static/'

# Prefix for default profile picture
DEFAULT_PROFILE_PICTURE_PREFIX = os.path.join(STATIC_URL, "img", "profile_default")

# Additional locations of static files
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT_DIRECTORY, 'files/static'),
)

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
    'compressor.finders.CompressorFinder',
)

COMPRESS_FILES = True
COMPRESS_OUTPUT_DIR = 'cache'
COMPRESS_PRECOMPILERS = (
    ('text/less', 'lessc {infile} {outfile}'),
)

COMPRESS_CSS_FILTERS = [
    'compressor.filters.css_default.CssAbsoluteFilter',
    # We want this later on, but it breaks production so disabling for now.
    #'compressor-filters.cssmin.CSSMinFilter',
]
COMPRESS_JS_FILTERS = [
    'compressor.filters.jsmin.JSMinFilter',
]

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'middleware.http.Http403Middleware',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'onlineweb4.urls'

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'onlineweb4.wsgi.application'

TEMPLATE_DIRS = (
    os.path.join(PROJECT_ROOT_DIRECTORY, 'templates/'),
)

# Pizzasystem settings
PIZZA_GROUP = 'dotkom'
PIZZA_ADMIN_GROUP = 'pizzaadmin'

# Grappelli settings
GRAPPELLI_ADMIN_TITLE = 'Onlineweb'

# List of usergroups that should be listed under "Finn brukere" in user profile
USER_SEARCH_GROUPS = [
    16,  # appKom
    1,   # arrKom
    2,   # banKom
    3,   # bedKom
    4,   # dotKom
    5,   # eksKom
    14,  # Eldsteradet
    6,   # fagKom
    11,  # Hovedstyret
    10,  # pangKom
    7,   # proKom
    8,   # triKom
    9,   # velKom
]

INSTALLED_APPS = (
    # Third party dependencies
    'django.contrib.humanize',
    'django_nose',
    'south',
    'django_notify', # Wiki
    'mptt', # Wiki
    'sekizai', # Wiki
    'sorl.thumbnail', # Wiki
    'grappelli',
    'filebrowser',
    'chunks',
    'crispy_forms',
    'django_extensions',
    'django_dynamic_fixture',
    'captcha',
    'compressor',
    'pdfdocument',
    'watson',
    'gunicorn',
    'markdown_deux',
    'djangoformsetjs',

    # Django apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.messages',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.staticfiles',

    # Onlineweb 4 apps
    'apps.api',
    'apps.article',
    'apps.authentication',
    'apps.autoconfig',
    'apps.careeropportunity',
    'apps.companyprofile',
    'apps.events',
    'apps.marks',
    'apps.offline',
    'apps.feedback',
    'apps.mommy',
    'apps.profiles',
    'apps.genfors',
    'apps.resourcecenter',
    'apps.mailinglists',
    'scripts',

    #External Apps
    'redWine'

    # Wiki
    'wiki',
    'wiki.plugins.attachments',
    'wiki.plugins.notifications',
    'wiki.plugins.images',
    'wiki.plugins.macros',
)

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'standard'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'feedback': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
        '': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

# crispy forms settings
CRISPY_TEMPLATE_PACK = 'bootstrap3'

# bootstrap messages classes
MESSAGE_TAGS = {messages.DEBUG: 'alert-debug',
                messages.INFO: 'alert-info',
                messages.SUCCESS: 'alert-success',
                messages.WARNING: 'alert-warning',
                messages.ERROR: 'alert-error'}

# Not really sure what this does.
# Has something to do with django-dynamic-fixture bumped from 1.6.4 to 1.6.5 in order to run a syncdb with mysql/postgres (OptimusCrime)
IMPORT_DDF_MODELS = False

# Required by the Wiki
TEMPLATE_CONTEXT_PROCESSORS = (
    "django.contrib.auth.context_processors.auth",
    "django.core.context_processors.debug",
    "django.core.context_processors.i18n",
    "django.core.context_processors.media",
    "django.core.context_processors.request",
    "django.core.context_processors.static",
    "django.core.context_processors.tz",
    "django.contrib.messages.context_processors.messages",
    "sekizai.context_processors.sekizai", # Wiki
    "onlineweb4.context_processors.analytics",
)

# Remember to keep 'local' last, so it can override any setting.
for settings_module in ['filebrowser', 'local']:  # local last
    if not os.path.exists(os.path.join(PROJECT_SETTINGS_DIRECTORY,
            settings_module + ".py")):
        sys.stderr.write("Could not find settings module '%s'.\n" %
                settings_module)
        if settings_module == 'local':
            sys.stderr.write("You need to copy the settings file "
                             "'onlineweb4/settings/example-local.py' to "
                             "'onlineweb4/settings/local.py'.\n")
        sys.exit(1)
    try:
        exec('from %s import *' % settings_module)
    except ImportError, e:
        print "Could not import settings for '%s' : %s" % (settings_module,
                str(e))
<commit_msg>Forgot comma<commit_after>import os
import sys
from django.contrib.messages import constants as messages

# Directory that contains this file.
PROJECT_SETTINGS_DIRECTORY = os.path.dirname(globals()['__file__'])
# Root directory. Contains manage.py
PROJECT_ROOT_DIRECTORY = os.path.join(PROJECT_SETTINGS_DIRECTORY, '..', '..')

TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
NOSE_ARGS = ['--with-coverage', '--cover-package=apps']

DEBUG = False
TEMPLATE_DEBUG = DEBUG

TEST_RUNNER = "django_nose.NoseTestSuiteRunner"

ADMINS = (
    ('dotKom', 'dotkom@online.ntnu.no'),
)
MANAGERS = ADMINS

# Email settings
DEFAULT_FROM_EMAIL = 'online@online.ntnu.no'
EMAIL_ARRKOM = 'arrkom@online.ntnu.no'
EMAIL_BEDKOM = 'bedkom@online.ntnu.no'
EMAIL_DOTKOM = 'dotkom@online.ntnu.no'
EMAIL_FAGKOM = 'fagkom@online.ntnu.no'
EMAIL_PROKOM = 'prokom@online.ntnu.no'
EMAIL_TRIKOM = 'trikom@online.ntnu.no'

# We will receive errors and other django messages from this email
SERVER_EMAIL = 'onlineweb4-error@online.ntnu.no'

TIME_ZONE = 'Europe/Oslo'

# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'nb'
LANGUAGES = (
                ('nb', 'Norwegian'),
                ('en_US', 'English'),
            )
LOCALE_PATHS = [
    os.path.join(PROJECT_ROOT_DIRECTORY, 'locale'),
]

SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
DATETIME_FORMAT = 'N j, Y, H:i'
SECRET_KEY = 'override-this-in-local.py'

# Session cookie expires after one year
SESSION_COOKIE_AGE = 31540000

# Override this in local if you need to :)
BASE_URL = 'https://online.ntnu.no'

AUTH_USER_MODEL = 'authentication.OnlineUser'
LOGIN_URL = '/auth/login/'

MEDIA_ROOT = os.path.join(PROJECT_ROOT_DIRECTORY, 'uploaded_media') # Override this in local.py in prod.
MEDIA_URL = '/media/'

STATIC_ROOT = os.path.join(PROJECT_ROOT_DIRECTORY, 'static')
STATIC_URL = '/static/'

# Prefix for default profile picture
DEFAULT_PROFILE_PICTURE_PREFIX = os.path.join(STATIC_URL, "img", "profile_default")

# Additional locations of static files
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT_DIRECTORY, 'files/static'),
)

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
    'compressor.finders.CompressorFinder',
)

COMPRESS_FILES = True
COMPRESS_OUTPUT_DIR = 'cache'
COMPRESS_PRECOMPILERS = (
    ('text/less', 'lessc {infile} {outfile}'),
)

COMPRESS_CSS_FILTERS = [
    'compressor.filters.css_default.CssAbsoluteFilter',
    # We want this later on, but it breaks production so disabling for now.
    #'compressor-filters.cssmin.CSSMinFilter',
]
COMPRESS_JS_FILTERS = [
    'compressor.filters.jsmin.JSMinFilter',
]

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'middleware.http.Http403Middleware',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'onlineweb4.urls'

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'onlineweb4.wsgi.application'

TEMPLATE_DIRS = (
    os.path.join(PROJECT_ROOT_DIRECTORY, 'templates/'),
)

# Pizzasystem settings
PIZZA_GROUP = 'dotkom'
PIZZA_ADMIN_GROUP = 'pizzaadmin'

# Grappelli settings
GRAPPELLI_ADMIN_TITLE = 'Onlineweb'

# List of usergroups that should be listed under "Finn brukere" in user profile
USER_SEARCH_GROUPS = [
    16,  # appKom
    1,   # arrKom
    2,   # banKom
    3,   # bedKom
    4,   # dotKom
    5,   # eksKom
    14,  # Eldsteradet
    6,   # fagKom
    11,  # Hovedstyret
    10,  # pangKom
    7,   # proKom
    8,   # triKom
    9,   # velKom
]

INSTALLED_APPS = (
    # Third party dependencies
    'django.contrib.humanize',
    'django_nose',
    'south',
    'django_notify', # Wiki
    'mptt', # Wiki
    'sekizai', # Wiki
    'sorl.thumbnail', # Wiki
    'grappelli',
    'filebrowser',
    'chunks',
    'crispy_forms',
    'django_extensions',
    'django_dynamic_fixture',
    'captcha',
    'compressor',
    'pdfdocument',
    'watson',
    'gunicorn',
    'markdown_deux',
    'djangoformsetjs',

    # Django apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.messages',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.staticfiles',

    # Onlineweb 4 apps
    'apps.api',
    'apps.article',
    'apps.authentication',
    'apps.autoconfig',
    'apps.careeropportunity',
    'apps.companyprofile',
    'apps.events',
    'apps.marks',
    'apps.offline',
    'apps.feedback',
    'apps.mommy',
    'apps.profiles',
    'apps.genfors',
    'apps.resourcecenter',
    'apps.mailinglists',
    'scripts',

    #External Apps
    'redWine',

    # Wiki
    'wiki',
    'wiki.plugins.attachments',
    'wiki.plugins.notifications',
    'wiki.plugins.images',
    'wiki.plugins.macros',
)

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'standard'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'feedback': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
        '': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

# crispy forms settings
CRISPY_TEMPLATE_PACK = 'bootstrap3'

# bootstrap messages classes
MESSAGE_TAGS = {messages.DEBUG: 'alert-debug',
                messages.INFO: 'alert-info',
                messages.SUCCESS: 'alert-success',
                messages.WARNING: 'alert-warning',
                messages.ERROR: 'alert-error'}

# Not really sure what this does.
# Has something to do with django-dynamic-fixture bumped from 1.6.4 to 1.6.5 in order to run a syncdb with mysql/postgres (OptimusCrime)
IMPORT_DDF_MODELS = False

# Required by the Wiki
TEMPLATE_CONTEXT_PROCESSORS = (
    "django.contrib.auth.context_processors.auth",
    "django.core.context_processors.debug",
    "django.core.context_processors.i18n",
    "django.core.context_processors.media",
    "django.core.context_processors.request",
    "django.core.context_processors.static",
    "django.core.context_processors.tz",
    "django.contrib.messages.context_processors.messages",
    "sekizai.context_processors.sekizai", # Wiki
    "onlineweb4.context_processors.analytics",
)

# Remember to keep 'local' last, so it can override any setting.
for settings_module in ['filebrowser', 'local']:  # local last
    if not os.path.exists(os.path.join(PROJECT_SETTINGS_DIRECTORY,
            settings_module + ".py")):
        sys.stderr.write("Could not find settings module '%s'.\n" %
                settings_module)
        if settings_module == 'local':
            sys.stderr.write("You need to copy the settings file "
                             "'onlineweb4/settings/example-local.py' to "
                             "'onlineweb4/settings/local.py'.\n")
        sys.exit(1)
    try:
        exec('from %s import *' % settings_module)
    except ImportError, e:
        print "Could not import settings for '%s' : %s" % (settings_module,
                str(e))
<|endoftext|>


<commit_before>"""
Allow us to make search queries
"""
import datetime

from django.contrib.contenttypes.models import ContentType
from django.db import models as djangomodels

from opal import models
from opal.core import fields

def get_model_name_from_column_name(column_name):
    return column_name.replace(' ', '').replace('_', '').lower()

def get_model_from_column_name(column_name):
    Mod = None
    model_name = get_model_name_from_column_name(column_name)

    for m in djangomodels.get_models():
        if m.__name__.lower() == model_name:
            if not Mod:
                Mod = m
            elif (issubclass(m, models.EpisodeSubrecord) or
                  issubclass(m, models.PatientSubrecord)):
                Mod = m

    return Mod

class PatientSummary(object):
    def __init__(self, episode):
        self.start_date = episode.start_date
        self.end_date = episode.end_date
        self.episode_ids = set([episode.id])
        self.episode_id = episode.id
        self.categories = set([episode.category])
        self.id = episode.patient.demographics_set.get().id

    def update(self, episode):
        if not self.start_date:
            self.start_date = episode.start_date
        elif episode.start_date:
            if self.start_date > episode.start_date:
                self.start_date = episode.start_date

        if not self.end_date:
            self.end_date = episode.end_date
        elif episode.end_date:
            if self.end_date < episode.end_date:
                self.end_date = episode.end_date

        self.episode_ids.add(episode.id)
        self.categories.add(episode.category)

    def to_dict(self):
        result = {k: getattr(self, k) for k in [
            "episode_id", "start_date", "end_date", "id"
        ]}
        result["categories"] = sorted(self.categories)
        result["count"] = len(self.episode_ids)
        return result

def episodes_for_user(episodes, user):
    """
    Given an iterable of EPISODES and a USER, return a filtered
    list of episodes that this user has the permissions to know
    about.
    """
    teams = models.Team.restricted_teams(user)
    allowed_episodes = []
    for e in episodes:
        allowed = False
        if e.tagging_set.count() == 0:
            allowed_episodes.append(e)
            continue
        for tagging in e.tagging_set.all():
            if not tagging.team.restricted:
                allowed = True
                break
            elif tagging.team in teams:
                allowed = True
                break

        if allowed:
            allowed_episodes.append(e)
    return allowed_episodes
>>>>>>> v0.4.3

class QueryBackend(object):
    """
    Base class for search implementations to inherit from
    """
    def __init__(self, user, query):
        self.user = user
        self.query = query

    def get_episodes(self):
        raise NotImplementedError()

    def description(self):
        raise NotImplementedError()

    def get_patients(self):
        raise NotImplementedError()

    def get_patient_summaries(self):
        raise NotImplementedError()

    def patients_as_json(self):
        patients = self.get_patients()
        return [p.to_dict(self.user) for p in patients]

class DatabaseQuery(QueryBackend):
    """
    The default built in query backend for OPAL allows advanced search
    criteria building.

    We broadly map reduce all criteria then the set of combined and/or
    criteria together, then only unique episodes.

    Finally we filter based on team restrictions.
    """

    def _episodes_for_boolean_fields(self, query, field, contains):
        model = query['column'].replace(' ', '_').lower()
        val = query['query'] == 'true'
        kw = {'{0}__{1}'.format(model.replace('_', ''), field): val}
        eps = models.Episode.objects.filter(**kw)
        return eps

    def _episodes_for_date_fields(self, query, field, contains):
        model = query['column'].replace(' ', '').lower()
        qtype = ''
        val = datetime.datetime.strptime(query['query'], "%d/%m/%Y")
        if query['queryType'] == 'Before':
            qtype = '__lte'
        elif query['queryType'] == 'After':
            qtype = '__gte'
        kw = {'{0}__{1}{2}'.format(model, field, qtype): val}
        eps = models.Episode.objects.filter(**kw)
        return eps

    def _episodes_for_fkorft_fields(self, query, field, contains, Mod):
        model = query['column'].replace(' ', '_').lower()

        # Look up to see if there is a synonym.
        content_type = ContentType.objects.get_for_model(
            getattr(Mod, field).foreign_model)
        name = query['query']
        try:
            from opal.models import Synonym
            synonym = Synonym.objects.get(content_type=content_type, name=name)
            name = synonym.content_object.name
        except Synonym.DoesNotExist: # maybe this is pointless exception bouncing?
            pass # That's fine.

        kw_fk = {'{0}__{1}_fk__name{2}'.format(model.replace('_', ''), field, contains): name}
        kw_ft = {'{0}__{1}_ft{2}'.format(model.replace('_', ''), field, contains): query['query']}

        if issubclass(Mod, models.EpisodeSubrecord):

            qs_fk = models.Episode.objects.filter(**kw_fk)
            qs_ft = models.Episode.objects.filter(**kw_ft)
            eps = set(list(qs_fk) + list(qs_ft))

        elif issubclass(Mod, models.PatientSubrecord):
            qs_fk = models.Patient.objects.filter(**kw_fk)
            qs_ft = models.Patient.objects.filter(**kw_ft)
            pats = set(list(qs_fk) + list(qs_ft))
            eps = []
            for p in pats:
                eps += list(p.episode_set.all())
        return eps

    def episodes_for_criteria(self, criteria):
        """
        Given one set of criteria, return episodes that match it.
        """
        query = criteria
        querytype = query['queryType']
        contains = '__iexact'
        if querytype == 'Contains':
            contains = '__icontains'

        column_name = query['column']
        field = query['field'].replace(' ', '_').lower()
        Mod = get_model_from_column_name(column_name)

        if column_name.lower() == 'tags':
            Mod = models.Tagging

        named_fields = [f for f in Mod._meta.fields if f.name == field]

        if len(named_fields) == 1 and isinstance(named_fields[0],djangomodels.BooleanField):
            eps = self._episodes_for_boolean_fields(query, field, contains)

        elif len(named_fields) == 1 and isinstance(named_fields[0], djangomodels.DateField):
            eps = self._episodes_for_date_fields(query, field, contains)

        elif hasattr(Mod, field) and isinstance(getattr(Mod, field), fields.ForeignKeyOrFreeText):
            eps = self._episodes_for_fkorft_fields(query, field, contains, Mod)

        else:
            model_name = get_model_name_from_column_name(query['column'])
            kw = {'{0}__{1}{2}'.format(model_name, field, contains): query['query']}

            if Mod == models.Tagging:
                eps = models.Episode.objects.ever_tagged(query['field'])

            elif issubclass(Mod, models.EpisodeSubrecord):
                eps = models.Episode.objects.filter(**kw)
            elif issubclass(Mod, models.PatientSubrecord):
                pats = models.Patient.objects.filter(**kw)
                eps = []
                for p in pats:
                    eps += list(p.episode_set.all())
        return eps

    def patients_for_criteria(self, criteria):
        """
            we get all patients for summaries by aggregating
            all related episodes.
        """
        episodes = episodes_for_criteria(criteria)
        return self._get_aggregate_patients_from_episodes(episodes)

    def _get_aggregate_patients_from_episodes(self, episodes):
        # at the moment we use date_of_admission/discharge only if
        # date_of_episode is null, because of this we can't do this
        # in a vanilla orm query
        patient_summaries = {}

        for episode in episodes:
            patient_id = episode.patient_id
            if patient_id in patient_summaries:
                patient_summaries[patient_id].update(episode)
            else:
                patient_summaries[patient_id] = PatientSummary(episode)

        patients = models.Patient.objects.filter(
            id__in=patient_summaries.keys()
        )
        patients = patients.prefetch_related("demographics_set")

        results = []

        for patient_id, patient_summary in patient_summaries.iteritems():
            patient = next(p for p in patients if p.id == patient_id)
            demographic = patient.demographics_set.get()

            result = {k: getattr(demographic, k) for k in [
                "name", "hospital_number", "date_of_birth"
                ]}

            result.update(patient_summary.to_dict())
            results.append(result)

        return results

    def _filter_for_restricted_only(self, episodes):
        """
        Given an iterable of EPISODES, return those for which our
        current restricted only user is allowed to know about.
        """
        teams = models.Team.restricted_teams(user)
        allowed_episodes = []
        for e in episodes:
            for tagging in e.tagging_set.all():
                if tagging.team in teams:
                    allowed_episodes.append(e)
                    break

        return allowed_episodes

    def _filter_restricted_teams(self, episodes):
        """
        Given an iterable of EPISODES, return only those which
        are not only members of restricted teams that our user is not
        allowed to know about.
        """
        return episodes_for_user(episodes, self.user)

    def _episodes_without_restrictions(self):
        all_matches = [(q['combine'], self.episodes_for_criteria(q))
                       for q in self.query]
        if not all_matches:
            return []

        working = set(all_matches[0][1])
        rest = all_matches[1:]

        for combine, episodes in rest:
            methods = {'and': 'intersection', 'or': 'union', 'not': 'difference'}
            working = getattr(set(episodes), methods[combine])(working)

        return working

    def _filter_restricted_episodes(self, eps):
        if self.user.profile.restricted_only:
            eps = self._filter_for_restricted_only(eps)
        else:
            eps = self._filter_restricted_teams(eps)

        return eps

    def get_episodes(self):
        eps = self._episodes_without_restrictions()
        return self._filter_restricted_episodes(eps)

    def get_patient_summaries(self):
        eps = self._episodes_without_restrictions()
        episode_ids = [e.id for e in eps]

        # get all episodes of patients, that have episodes that
        # match the criteria
        all_eps = models.Episode.objects.filter(
            patient__episode__in=episode_ids
        )
        filtered_eps = self._filter_restricted_episodes(all_eps)
        return self._get_aggregate_patients_from_episodes(filtered_eps)

    def get_patients(self):
        patients = set(e.patient for e in self.get_episodes())
        return list(patients)

    def description(self):
        """
        Provide a textual description of the current search
        """
        filters = "\n".join("{combine} {column} {field} {queryType} {query}".format(**f) for f in self.query)
        return """{username} ({date})
Searching for:
{filters}
""".format(username=self.user.username, date=datetime.datetime.now(), filters=filters)

SearchBackend = DatabaseQuery
<commit_msg>fixing merge conflicts from v0.4.3<commit_after>"""
Allow us to make search queries
"""
import datetime

from django.contrib.contenttypes.models import ContentType
from django.db import models as djangomodels

from opal import models
from opal.core import fields

def get_model_name_from_column_name(column_name):
    return column_name.replace(' ', '').replace('_', '').lower()

def get_model_from_column_name(column_name):
    Mod = None
    model_name = get_model_name_from_column_name(column_name)

    for m in djangomodels.get_models():
        if m.__name__.lower() == model_name:
            if not Mod:
                Mod = m
            elif (issubclass(m, models.EpisodeSubrecord) or
                  issubclass(m, models.PatientSubrecord)):
                Mod = m

    return Mod

class PatientSummary(object):
    def __init__(self, episode):
        self.start_date = episode.start_date
        self.end_date = episode.end_date
        self.episode_ids = set([episode.id])
        self.episode_id = episode.id
        self.categories = set([episode.category])
        self.id = episode.patient.demographics_set.get().id

    def update(self, episode):
        if not self.start_date:
            self.start_date = episode.start_date
        elif episode.start_date:
            if self.start_date > episode.start_date:
                self.start_date = episode.start_date

        if not self.end_date:
            self.end_date = episode.end_date
        elif episode.end_date:
            if self.end_date < episode.end_date:
                self.end_date = episode.end_date

        self.episode_ids.add(episode.id)
        self.categories.add(episode.category)

    def to_dict(self):
        result = {k: getattr(self, k) for k in [
            "episode_id", "start_date", "end_date", "id"
        ]}
        result["categories"] = sorted(self.categories)
        result["count"] = len(self.episode_ids)
        return result

def episodes_for_user(episodes, user):
    """
    Given an iterable of EPISODES and a USER, return a filtered
    list of episodes that this user has the permissions to know
    about.
    """
    teams = models.Team.restricted_teams(user)
    allowed_episodes = []
    for e in episodes:
        allowed = False
        if e.tagging_set.count() == 0:
            allowed_episodes.append(e)
            continue
        for tagging in e.tagging_set.all():
            if not tagging.team.restricted:
                allowed = True
                break
            elif tagging.team in teams:
                allowed = True
                break

        if allowed:
            allowed_episodes.append(e)
    return allowed_episodes

class QueryBackend(object):
    """
    Base class for search implementations to inherit from
    """
    def __init__(self, user, query):
        self.user = user
        self.query = query

    def get_episodes(self):
        raise NotImplementedError()

    def description(self):
        raise NotImplementedError()

    def get_patients(self):
        raise NotImplementedError()

    def get_patient_summaries(self):
        raise NotImplementedError()

    def patients_as_json(self):
        patients = self.get_patients()
        return [p.to_dict(self.user) for p in patients]

class DatabaseQuery(QueryBackend):
    """
    The default built in query backend for OPAL allows advanced search
    criteria building.

    We broadly map reduce all criteria then the set of combined and/or
    criteria together, then only unique episodes.

    Finally we filter based on team restrictions.
    """

    def _episodes_for_boolean_fields(self, query, field, contains):
        model = query['column'].replace(' ', '_').lower()
        val = query['query'] == 'true'
        kw = {'{0}__{1}'.format(model.replace('_', ''), field): val}
        eps = models.Episode.objects.filter(**kw)
        return eps

    def _episodes_for_date_fields(self, query, field, contains):
        model = query['column'].replace(' ', '').lower()
        qtype = ''
        val = datetime.datetime.strptime(query['query'], "%d/%m/%Y")
        if query['queryType'] == 'Before':
            qtype = '__lte'
        elif query['queryType'] == 'After':
            qtype = '__gte'
        kw = {'{0}__{1}{2}'.format(model, field, qtype): val}
        eps = models.Episode.objects.filter(**kw)
        return eps

    def _episodes_for_fkorft_fields(self, query, field, contains, Mod):
        model = query['column'].replace(' ', '_').lower()

        # Look up to see if there is a synonym.
        content_type = ContentType.objects.get_for_model(
            getattr(Mod, field).foreign_model)
        name = query['query']
        try:
            from opal.models import Synonym
            synonym = Synonym.objects.get(content_type=content_type, name=name)
            name = synonym.content_object.name
        except Synonym.DoesNotExist: # maybe this is pointless exception bouncing?
            pass # That's fine.

        kw_fk = {'{0}__{1}_fk__name{2}'.format(model.replace('_', ''), field, contains): name}
        kw_ft = {'{0}__{1}_ft{2}'.format(model.replace('_', ''), field, contains): query['query']}

        if issubclass(Mod, models.EpisodeSubrecord):

            qs_fk = models.Episode.objects.filter(**kw_fk)
            qs_ft = models.Episode.objects.filter(**kw_ft)
            eps = set(list(qs_fk) + list(qs_ft))

        elif issubclass(Mod, models.PatientSubrecord):
            qs_fk = models.Patient.objects.filter(**kw_fk)
            qs_ft = models.Patient.objects.filter(**kw_ft)
            pats = set(list(qs_fk) + list(qs_ft))
            eps = []
            for p in pats:
                eps += list(p.episode_set.all())
        return eps

    def episodes_for_criteria(self, criteria):
        """
        Given one set of criteria, return episodes that match it.
        """
        query = criteria
        querytype = query['queryType']
        contains = '__iexact'
        if querytype == 'Contains':
            contains = '__icontains'

        column_name = query['column']
        field = query['field'].replace(' ', '_').lower()
        Mod = get_model_from_column_name(column_name)

        if column_name.lower() == 'tags':
            Mod = models.Tagging

        named_fields = [f for f in Mod._meta.fields if f.name == field]

        if len(named_fields) == 1 and isinstance(named_fields[0],djangomodels.BooleanField):
            eps = self._episodes_for_boolean_fields(query, field, contains)

        elif len(named_fields) == 1 and isinstance(named_fields[0], djangomodels.DateField):
            eps = self._episodes_for_date_fields(query, field, contains)

        elif hasattr(Mod, field) and isinstance(getattr(Mod, field), fields.ForeignKeyOrFreeText):
            eps = self._episodes_for_fkorft_fields(query, field, contains, Mod)

        else:
            model_name = get_model_name_from_column_name(query['column'])
            kw = {'{0}__{1}{2}'.format(model_name, field, contains): query['query']}

            if Mod == models.Tagging:
                eps = models.Episode.objects.ever_tagged(query['field'])

            elif issubclass(Mod, models.EpisodeSubrecord):
                eps = models.Episode.objects.filter(**kw)
            elif issubclass(Mod, models.PatientSubrecord):
                pats = models.Patient.objects.filter(**kw)
                eps = []
                for p in pats:
                    eps += list(p.episode_set.all())
        return eps

    def patients_for_criteria(self, criteria):
        """
            we get all patients for summaries by aggregating
            all related episodes.
        """
        episodes = episodes_for_criteria(criteria)
        return self._get_aggregate_patients_from_episodes(episodes)

    def _get_aggregate_patients_from_episodes(self, episodes):
        # at the moment we use date_of_admission/discharge only if
        # date_of_episode is null, because of this we can't do this
        # in a vanilla orm query
        patient_summaries = {}

        for episode in episodes:
            patient_id = episode.patient_id
            if patient_id in patient_summaries:
                patient_summaries[patient_id].update(episode)
            else:
                patient_summaries[patient_id] = PatientSummary(episode)

        patients = models.Patient.objects.filter(
            id__in=patient_summaries.keys()
        )
        patients = patients.prefetch_related("demographics_set")

        results = []

        for patient_id, patient_summary in patient_summaries.iteritems():
            patient = next(p for p in patients if p.id == patient_id)
            demographic = patient.demographics_set.get()

            result = {k: getattr(demographic, k) for k in [
                "name", "hospital_number", "date_of_birth"
                ]}

            result.update(patient_summary.to_dict())
            results.append(result)

        return results

    def _filter_for_restricted_only(self, episodes):
        """
        Given an iterable of EPISODES, return those for which our
        current restricted only user is allowed to know about.
        """
        teams = models.Team.restricted_teams(user)
        allowed_episodes = []
        for e in episodes:
            for tagging in e.tagging_set.all():
                if tagging.team in teams:
                    allowed_episodes.append(e)
                    break

        return allowed_episodes

    def _filter_restricted_teams(self, episodes):
        """
        Given an iterable of EPISODES, return only those which
        are not only members of restricted teams that our user is not
        allowed to know about.
        """
        return episodes_for_user(episodes, self.user)

    def _episodes_without_restrictions(self):
        all_matches = [(q['combine'], self.episodes_for_criteria(q))
                       for q in self.query]
        if not all_matches:
            return []

        working = set(all_matches[0][1])
        rest = all_matches[1:]

        for combine, episodes in rest:
            methods = {'and': 'intersection', 'or': 'union', 'not': 'difference'}
            working = getattr(set(episodes), methods[combine])(working)

        return working

    def _filter_restricted_episodes(self, eps):
        if self.user.profile.restricted_only:
            eps = self._filter_for_restricted_only(eps)
        else:
            eps = self._filter_restricted_teams(eps)

        return eps

    def get_episodes(self):
        eps = self._episodes_without_restrictions()
        return self._filter_restricted_episodes(eps)

    def get_patient_summaries(self):
        eps = self._episodes_without_restrictions()
        episode_ids = [e.id for e in eps]

        # get all episodes of patients, that have episodes that
        # match the criteria
        all_eps = models.Episode.objects.filter(
            patient__episode__in=episode_ids
        )
        filtered_eps = self._filter_restricted_episodes(all_eps)
        return self._get_aggregate_patients_from_episodes(filtered_eps)

    def get_patients(self):
        patients = set(e.patient for e in self.get_episodes())
        return list(patients)

    def description(self):
        """
        Provide a textual description of the current search
        """
        filters = "\n".join("{combine} {column} {field} {queryType} {query}".format(**f) for f in self.query)
        return """{username} ({date})
Searching for:
{filters}
""".format(username=self.user.username, date=datetime.datetime.now(), filters=filters)

SearchBackend = DatabaseQuery
<|endoftext|>