<commit_before># -*- coding: latin-1 -*-
# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Module containing tests for keyboard module"""

from __future__ import unicode_literals
from __future__ import print_function

import sys
import os
import unittest
import subprocess
import time
sys.path.append(".")
if sys.platform == 'win32':
    from pywinauto.keyboard import send_keys, parse_keys, KeySequenceError
    from pywinauto.keyboard import KeyAction, VirtualKeyAction, PauseAction
    from pywinauto.sysinfo import is_x64_Python, is_x64_OS
    from pywinauto.application import Application
else:
    from pywinauto import mouse
    from pywinauto.linux.keyboard import send_keys, KeySequenceError, KeyAction
    from pywinauto.linux import clipboard

def mfc_samples():
    mfc_samples_folder = os.path.join(
       os.path.dirname(__file__), r"..\..\apps\MFC_samples")
    if is_x64_Python():
        mfc_samples_folder = os.path.join(mfc_samples_folder, 'x64')
    return mfc_samples_folder

def _notepad_exe():
    if is_x64_Python() or not is_x64_OS():
        return r"C:\Windows\System32\notepad.exe"
    else:
        return r"C:\Windows\SysWOW64\notepad.exe"

def _test_app():
    test_folder = os.path.join(os.path.dirname
                               (os.path.dirname
                                (os.path.dirname
                                 (os.path.abspath(__file__)))),
                               r"apps/SendKeysTester")
    return os.path.join(test_folder, r"send_keys_test_app")

class SendKeysTests(unittest.TestCase):
    """Unit tests for the Sendkeys module"""

    def setUp(self):
        """Start the application set some data and ensure the application is in the state we want it."""
        if sys.platform == 'win32':
            self.app = Application()
            self.app.start(_notepad_exe())
            self.dlg = self.app.UntitledNotepad
            self.ctrl = self.dlg.Edit
        else:
            self.app = subprocess.Popen("exec " + _test_app(), shell=True)
            time.sleep(0.1)
            mouse.click(coords=(300, 300))
            time.sleep(0.1)

    def tearDown(self):
        """Close the application after tests"""
        if sys.platform == 'win32':
            try:
                self.dlg.close(0.1)
            except Exception: # TimeoutError:
                pass
            try:
                if self.app.Notepad["Do&n't Save"].exists():
                    self.app.Notepad["Do&n't Save"].click()
                    self.app.Notepad["Do&n't Save"].wait_not('visible')
            except Exception: # TimeoutError:
                pass
            finally:
                if self.dlg.exists(timeout=0.1):
                    self.app.kill()
        else:
            # call Popen.kill() on Linux since Application.kill() is not implemented yet
            self.app.kill()

    def receive_text(self):
        """Receive data from text field"""
        received = ' '
        if sys.platform == 'win32':
            received = self.ctrl.text_block()
        else:
            time.sleep(0.2)
            send_keys('^a')
            time.sleep(0.2)
            send_keys('^c')
            send_keys('{RIGHT}')
            received = clipboard.get_data()
        return received

    def __run_NormalCharacters_with_options(self, **args):
        """Make sure that sending any character in range """
        #unused var: missed = []
        for i in range(32, 127):

            # skip characters that must be escaped
            if chr(i) in '~!@#$%^&*()_+{}|:"<>? ':
                continue

            send_keys(chr(i), pause = .001, **args)
            received = self.receive_text()[-1]

            self.assertEqual(i, ord(received))

    # Space tests
    def testNormalWithSpaces(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_spaces = True)

    def testNormalWithoutSpaces(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_spaces = False)

    def testSpaceWithSpaces(self):
        """Make sure that with spaces option works"""
        send_keys(" \t \t ", pause = .001, with_spaces = True)
        received = self.receive_text()
        self.assertEqual("   ", received)

    def testSpaceWithoutSpaces(self):
        """Make sure that with spaces option works"""
        send_keys(" \t \t ", pause = .001, with_spaces = False)
        received = self.receive_text()
        self.assertEqual("", received)

    # Tab tests
    def testNormalWithTabs(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_tabs = True)

    def testNormalWithoutTabs(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_tabs = False)

    def testTabWithTabs(self):
        """Make sure that with spaces option works"""
        send_keys("\t \t \t", pause = .1, with_tabs = True)
        received = self.receive_text()
        self.assertEqual("\t\t\t", received)

    def testTabWithoutTabs(self):
        """Make sure that with spaces option works"""
        send_keys("\t a\t b\t", pause = .1, with_tabs = False)
        received = self.receive_text()
        self.assertEqual("ab", received)

    def testTab(self):
        """Make sure that with spaces option works"""
        send_keys("{TAB}  {TAB} ", pause = .3)
        received = self.receive_text()
        self.assertEqual("\t\t", received)

    # Newline tests
    def testNormalWithNewlines(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_newlines = True)

    def testNormalWithoutNewlines(self):
        """Make sure that with_newlines option works"""
        self.__run_NormalCharacters_with_options(with_newlines = False)

    def testNewlinesWithNewlines(self):
        """Make sure that with_newlines option works"""
        send_keys("\t \t \t a~\tb\nc", pause = .5, with_newlines = True)
        received = self.receive_text()
        if sys.platform == 'win32':
            self.assertEqual("a\r\nb\r\nc", received)
        else:
            self.assertEqual("a\nb\nc", received)

    def testNewlinesWithoutNewlines(self):
        """"Make sure that with_newlines option works"""
        send_keys("\t \t \t\na", pause = .01, with_newlines = False)
        received = self.receive_text()
        self.assertEqual("a", received)

    #def testANSIExtendedCharacters(self):
    #    "Make sure that sending any character in range "
    #    #self.cmd = Application()
    #    #self.cmd.start("cmd.exe", create_new_console=True, wait_for_idle=False)
    #    ActionLogger().log('Preferred encoding: ' + locale.getpreferredencoding())
    #
    #    #os.system("chcp 850")
    #    matched = 0
    #    extended_chars = b"\x81\x82\x83\xa1\xe1\xff"
    #    for char in extended_chars:

    #        if six.PY3:
    #            c = str(char)
    #        else:
    #            c = char.decode(locale.getpreferredencoding()) #'cp850')
    #        send_keys(c, pause = .01)
    #        received = self.receive_text()[-1]

    #        if c == received:
    #            matched += 1
    #        else:
    #            print("expected %s, recieved %s"% (
    #                repr(c), repr(received)))

    #    self.assertEqual(matched, len(extended_chars))

    def testCharsThatMustBeEscaped(self):
        """Make sure that escaping characters works"""
        send_keys("{%}{^}{+}{(}{)}{{}{}}{~}")
        received = self.receive_text()
        self.assertEqual("%^+(){}~", received)

    def testIncorrectCases(self):
        """Make sure that incorrect key sequences raise an exception"""
        self.assertRaises(KeySequenceError, send_keys, "{ENTER")
        self.assertRaises(KeySequenceError, send_keys, "ENTER)")
        self.assertRaises(RuntimeError, send_keys, "%{Enterius}")
        self.assertRaises(KeySequenceError, send_keys, "{PAUSE small}")

        try:
            send_keys("{ENTER five}")
        except KeySequenceError as exc:
            self.assertEqual("invalid repetition count five", str(exc))

        try:
            send_keys("ENTER}")
        except KeySequenceError as exc:
            self.assertEqual("`}` should be preceeded by `{`", str(exc))

    def testKeyDescription(self):
        """Test KeyAction._"""
        self.assertEqual("<X>", str(KeyAction("X")))
        self.assertEqual("<Y down>", str(KeyAction("Y", up=False)))
        self.assertEqual("<Y up>", str(KeyAction("Y", down=False)))
        #self.assertEqual("<ENTER>", str(VirtualKeyAction(13))) # == "<VK_RETURN>" in Python 2.7 (TODO)
        if sys.platform == 'win32':
            self.assertEqual("<PAUSE 1.00>", str(PauseAction(1.0)))

    def testRepetition(self):
        """Make sure that repeated action works"""
        send_keys("{TAB 3}{PAUSE 0.5}{F 3}", pause = .3)
        received = self.receive_text()
        self.assertEqual("\t\t\tFFF", received)

    def testShiftModifier(self):
        """Make sure that Shift modifier works"""
        send_keys("+(a)")
        received = self.receive_text()
        self.assertEqual("A", received)

    if sys.platform != 'win32':
        def testAltModifier(self):
            """Make sure that alt modifier works"""
            clipboard.set_data('abc')
            # check alt via opening edit menu and paste text from clipboard
            time.sleep(0.3)
            send_keys('%(e)')
            time.sleep(0.3)
            send_keys('{ENTER}')
            received = self.receive_text()
            self.assertEqual('abc', received)

if sys.platform == 'win32':
    class SendKeysModifiersTests(unittest.TestCase):
        """Unit tests for the Sendkeys module (modifiers)"""

        def setUp(self):
            """Start the application and ensure it's in the state we want"""
            self.app = Application().start(os.path.join(mfc_samples(), u"CtrlTest.exe"))
            self.dlg = self.app.Control_Test_App

        def tearDown(self):
            """Close the application after tests"""
            try:
                self.dlg.close(0.5)
            except Exception:
                pass
            finally:
                self.app.kill()

        def testModifiersForFewChars(self):
            """Make sure that repeated action works"""
            send_keys("%(SC)", pause = .3)
            dlg = self.app.window(title='Using C++ Derived Class')
            dlg.wait('ready')
            dlg.Done.close_click()
            dlg.wait_not('visible')

            send_keys("%(H{LEFT}{UP}{ENTER})", pause = .3)
            dlg = self.app.window(title='Sample Dialog with spin controls')
            dlg.wait('ready')
            dlg.Done.close_click()
            dlg.wait_not('visible')

if sys.platform == 'win32':
    class VkPacketTests(unittest.TestCase):
        def testBasic(self):
            keys = parse_keys('AAA', vk_packet=False)
            self.assertEqual(3, len(keys))
            for key in keys:
                self.assertTrue(isinstance(key, VirtualKeyAction))
                wVk, wScan, dwFlags = key._get_key_info()
                self.assertEqual(ord('A'), wVk)
                self.assertEqual(0, dwFlags)

        def testRepeat(self):
            keys = parse_keys('{A 3}', vk_packet=False)
            self.assertEqual(3, len(keys))
            for key in keys:
                self.assertTrue(isinstance(key, VirtualKeyAction))
                wVk, wScan, dwFlags = key._get_key_info()
                self.assertEqual(ord('A'), wVk)
                self.assertEqual(0, dwFlags)

        def testSymbol(self):
            key, = parse_keys('{=}', vk_packet=False)
            self.assertTrue(isinstance(key, VirtualKeyAction))
            wVk, wScan, dwFlags = key._get_key_info()
            self.assertEqual(0xbb, wVk)
            self.assertEqual(0, dwFlags)

        def testNoVk(self):
            key, = parse_keys('!', vk_packet=False)
            self.assertTrue(isinstance(key, KeyAction))
            wVk, wScan, dwFlags = key._get_key_info()
            self.assertEqual(0, wVk)

#====================================================================
if __name__ == "__main__":
    unittest.main()

    #import doctest
    #doctest.testmod()
<commit_msg>Refactor SendKeysTests.setUp()<commit_after># -*- coding: latin-1 -*-
# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Module containing tests for keyboard module"""

from __future__ import unicode_literals
from __future__ import print_function

import sys
import os
import unittest
import subprocess
import time
sys.path.append(".")
from pywinauto.application import Application
if sys.platform == 'win32':
    from pywinauto.keyboard import send_keys, parse_keys, KeySequenceError
    from pywinauto.keyboard import KeyAction, VirtualKeyAction, PauseAction
    from pywinauto.sysinfo import is_x64_Python, is_x64_OS
else:
    from pywinauto import mouse
    from pywinauto.linux.keyboard import send_keys, KeySequenceError, KeyAction
    from pywinauto.linux import clipboard

def mfc_samples():
    mfc_samples_folder = os.path.join(
       os.path.dirname(__file__), r"..\..\apps\MFC_samples")
    if is_x64_Python():
        mfc_samples_folder = os.path.join(mfc_samples_folder, 'x64')
    return mfc_samples_folder

def _notepad_exe():
    if is_x64_Python() or not is_x64_OS():
        return r"C:\Windows\System32\notepad.exe"
    else:
        return r"C:\Windows\SysWOW64\notepad.exe"

def _test_app():
    test_folder = os.path.join(os.path.dirname
                               (os.path.dirname
                                (os.path.dirname
                                 (os.path.abspath(__file__)))),
                               r"apps/SendKeysTester")
    return os.path.join(test_folder, r"send_keys_test_app")

class SendKeysTests(unittest.TestCase):
    """Unit tests for the Sendkeys module"""

    def setUp(self):
        """Start the application set some data and ensure the application is in the state we want it."""
        self.app = Application()
        if sys.platform == 'win32':
            self.app.start(_notepad_exe())
            self.dlg = self.app.UntitledNotepad
            self.ctrl = self.dlg.Edit
        else:
            self.app.start(_test_app())
            time.sleep(0.1)
            mouse.click(coords=(300, 300))
            time.sleep(0.1)

    def tearDown(self):
        """Close the application after tests"""
        if sys.platform == 'win32':
            try:
                self.dlg.close(0.1)
            except Exception: # TimeoutError:
                pass
            try:
                if self.app.Notepad["Do&n't Save"].exists():
                    self.app.Notepad["Do&n't Save"].click()
                    self.app.Notepad["Do&n't Save"].wait_not('visible')
            except Exception: # TimeoutError:
                pass
            finally:
                if self.dlg.exists(timeout=0.1):
                    self.app.kill()
        else:
            # call Popen.kill() on Linux since Application.kill() is not implemented yet
            self.app.kill()

    def receive_text(self):
        """Receive data from text field"""
        received = ' '
        if sys.platform == 'win32':
            received = self.ctrl.text_block()
        else:
            time.sleep(0.2)
            send_keys('^a')
            time.sleep(0.2)
            send_keys('^c')
            send_keys('{RIGHT}')
            received = clipboard.get_data()
        return received

    def __run_NormalCharacters_with_options(self, **args):
        """Make sure that sending any character in range """
        #unused var: missed = []
        for i in range(32, 127):

            # skip characters that must be escaped
            if chr(i) in '~!@#$%^&*()_+{}|:"<>? ':
                continue

            send_keys(chr(i), pause = .001, **args)
            received = self.receive_text()[-1]

            self.assertEqual(i, ord(received))

    # Space tests
    def testNormalWithSpaces(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_spaces = True)

    def testNormalWithoutSpaces(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_spaces = False)

    def testSpaceWithSpaces(self):
        """Make sure that with spaces option works"""
        send_keys(" \t \t ", pause = .001, with_spaces = True)
        received = self.receive_text()
        self.assertEqual("   ", received)

    def testSpaceWithoutSpaces(self):
        """Make sure that with spaces option works"""
        send_keys(" \t \t ", pause = .001, with_spaces = False)
        received = self.receive_text()
        self.assertEqual("", received)

    # Tab tests
    def testNormalWithTabs(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_tabs = True)

    def testNormalWithoutTabs(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_tabs = False)

    def testTabWithTabs(self):
        """Make sure that with spaces option works"""
        send_keys("\t \t \t", pause = .1, with_tabs = True)
        received = self.receive_text()
        self.assertEqual("\t\t\t", received)

    def testTabWithoutTabs(self):
        """Make sure that with spaces option works"""
        send_keys("\t a\t b\t", pause = .1, with_tabs = False)
        received = self.receive_text()
        self.assertEqual("ab", received)

    def testTab(self):
        """Make sure that with spaces option works"""
        send_keys("{TAB}  {TAB} ", pause = .3)
        received = self.receive_text()
        self.assertEqual("\t\t", received)

    # Newline tests
    def testNormalWithNewlines(self):
        """Make sure that with spaces option works"""
        self.__run_NormalCharacters_with_options(with_newlines = True)

    def testNormalWithoutNewlines(self):
        """Make sure that with_newlines option works"""
        self.__run_NormalCharacters_with_options(with_newlines = False)

    def testNewlinesWithNewlines(self):
        """Make sure that with_newlines option works"""
        send_keys("\t \t \t a~\tb\nc", pause = .5, with_newlines = True)
        received = self.receive_text()
        if sys.platform == 'win32':
            self.assertEqual("a\r\nb\r\nc", received)
        else:
            self.assertEqual("a\nb\nc", received)

    def testNewlinesWithoutNewlines(self):
        """"Make sure that with_newlines option works"""
        send_keys("\t \t \t\na", pause = .01, with_newlines = False)
        received = self.receive_text()
        self.assertEqual("a", received)

    #def testANSIExtendedCharacters(self):
    #    "Make sure that sending any character in range "
    #    #self.cmd = Application()
    #    #self.cmd.start("cmd.exe", create_new_console=True, wait_for_idle=False)
    #    ActionLogger().log('Preferred encoding: ' + locale.getpreferredencoding())
    #
    #    #os.system("chcp 850")
    #    matched = 0
    #    extended_chars = b"\x81\x82\x83\xa1\xe1\xff"
    #    for char in extended_chars:

    #        if six.PY3:
    #            c = str(char)
    #        else:
    #            c = char.decode(locale.getpreferredencoding()) #'cp850')
    #        send_keys(c, pause = .01)
    #        received = self.receive_text()[-1]

    #        if c == received:
    #            matched += 1
    #        else:
    #            print("expected %s, recieved %s"% (
    #                repr(c), repr(received)))

    #    self.assertEqual(matched, len(extended_chars))

    def testCharsThatMustBeEscaped(self):
        """Make sure that escaping characters works"""
        send_keys("{%}{^}{+}{(}{)}{{}{}}{~}")
        received = self.receive_text()
        self.assertEqual("%^+(){}~", received)

    def testIncorrectCases(self):
        """Make sure that incorrect key sequences raise an exception"""
        self.assertRaises(KeySequenceError, send_keys, "{ENTER")
        self.assertRaises(KeySequenceError, send_keys, "ENTER)")
        self.assertRaises(RuntimeError, send_keys, "%{Enterius}")
        self.assertRaises(KeySequenceError, send_keys, "{PAUSE small}")

        try:
            send_keys("{ENTER five}")
        except KeySequenceError as exc:
            self.assertEqual("invalid repetition count five", str(exc))

        try:
            send_keys("ENTER}")
        except KeySequenceError as exc:
            self.assertEqual("`}` should be preceeded by `{`", str(exc))

    def testKeyDescription(self):
        """Test KeyAction._"""
        self.assertEqual("<X>", str(KeyAction("X")))
        self.assertEqual("<Y down>", str(KeyAction("Y", up=False)))
        self.assertEqual("<Y up>", str(KeyAction("Y", down=False)))
        #self.assertEqual("<ENTER>", str(VirtualKeyAction(13))) # == "<VK_RETURN>" in Python 2.7 (TODO)
        if sys.platform == 'win32':
            self.assertEqual("<PAUSE 1.00>", str(PauseAction(1.0)))

    def testRepetition(self):
        """Make sure that repeated action works"""
        send_keys("{TAB 3}{PAUSE 0.5}{F 3}", pause = .3)
        received = self.receive_text()
        self.assertEqual("\t\t\tFFF", received)

    def testShiftModifier(self):
        """Make sure that Shift modifier works"""
        send_keys("+(a)")
        received = self.receive_text()
        self.assertEqual("A", received)

    if sys.platform != 'win32':
        def testAltModifier(self):
            """Make sure that alt modifier works"""
            clipboard.set_data('abc')
            # check alt via opening edit menu and paste text from clipboard
            time.sleep(0.3)
            send_keys('%(e)')
            time.sleep(0.3)
            send_keys('{ENTER}')
            received = self.receive_text()
            self.assertEqual('abc', received)

if sys.platform == 'win32':
    class SendKeysModifiersTests(unittest.TestCase):
        """Unit tests for the Sendkeys module (modifiers)"""

        def setUp(self):
            """Start the application and ensure it's in the state we want"""
            self.app = Application().start(os.path.join(mfc_samples(), u"CtrlTest.exe"))
            self.dlg = self.app.Control_Test_App

        def tearDown(self):
            """Close the application after tests"""
            try:
                self.dlg.close(0.5)
            except Exception:
                pass
            finally:
                self.app.kill()

        def testModifiersForFewChars(self):
            """Make sure that repeated action works"""
            send_keys("%(SC)", pause = .3)
            dlg = self.app.window(title='Using C++ Derived Class')
            dlg.wait('ready')
            dlg.Done.close_click()
            dlg.wait_not('visible')

            send_keys("%(H{LEFT}{UP}{ENTER})", pause = .3)
            dlg = self.app.window(title='Sample Dialog with spin controls')
            dlg.wait('ready')
            dlg.Done.close_click()
            dlg.wait_not('visible')

if sys.platform == 'win32':
    class VkPacketTests(unittest.TestCase):
        def testBasic(self):
            keys = parse_keys('AAA', vk_packet=False)
            self.assertEqual(3, len(keys))
            for key in keys:
                self.assertTrue(isinstance(key, VirtualKeyAction))
                wVk, wScan, dwFlags = key._get_key_info()
                self.assertEqual(ord('A'), wVk)
                self.assertEqual(0, dwFlags)

        def testRepeat(self):
            keys = parse_keys('{A 3}', vk_packet=False)
            self.assertEqual(3, len(keys))
            for key in keys:
                self.assertTrue(isinstance(key, VirtualKeyAction))
                wVk, wScan, dwFlags = key._get_key_info()
                self.assertEqual(ord('A'), wVk)
                self.assertEqual(0, dwFlags)

        def testSymbol(self):
            key, = parse_keys('{=}', vk_packet=False)
            self.assertTrue(isinstance(key, VirtualKeyAction))
            wVk, wScan, dwFlags = key._get_key_info()
            self.assertEqual(0xbb, wVk)
            self.assertEqual(0, dwFlags)

        def testNoVk(self):
            key, = parse_keys('!', vk_packet=False)
            self.assertTrue(isinstance(key, KeyAction))
            wVk, wScan, dwFlags = key._get_key_info()
            self.assertEqual(0, wVk)

#====================================================================
if __name__ == "__main__":
    unittest.main()

    #import doctest
    #doctest.testmod()
<|endoftext|>


<commit_before>"""
This module defines common classes and interfaces for manual labour
"""

import json
import pkg_resources
import hashlib
from copy import copy
from os.path import join
from datetime import timedelta

import jsonschema

SCHEMA_DIR =  pkg_resources.resource_filename('manuallabour.core','schema')

def dereference_schema(schema_dir,schema):
    """
    Dereference JSON references.

    Only works for relative filesystem references like
    "$ref" : "sibling.json#/schemaname"
    and can not handle recursive references.
    """
    for key,val in schema.iteritems():
        if key == "$ref":
            fname,sname = val.split('#/')
            with open(join(schema_dir,fname)) as fid:
                ref_schema = json.loads(fid.read())
            schema.update(ref_schema[sname])
            schema.pop("$ref")
            return
        elif isinstance(val,dict):
            dereference_schema(schema_dir,val)
        elif isinstance(val,list):
            for sub in val:
                if isinstance(sub,dict):
                    dereference_schema(schema_dir,sub)

def load_schema(schema_dir,schema_name):
    """
    Load a jsonschema and dereferences JSON references by substitution.
    """
    with open(join(schema_dir,schema_name)) as fid:
        schema = json.loads(fid.read())
    dereference_schema(schema_dir,schema)
    return schema

class DataStruct(object):
    """
    A validating convenience wrapper around a dictionary. Serves as basis for
    many of the classes holding data.
    """
    _schema = None
    """JSON schema for the input of this class"""
    _validator = None
    """Validator for the schema of this class"""
    def __init__(self,**kwargs):
        self._validator.validate(kwargs)
        self._kwargs = kwargs
        self._calculated = {}
        for field, schema in self._schema["properties"].iteritems():
            #check for missing defaults
            if (not field in self._schema.get("required",[])) and \
                not "default" in schema:
                raise ValueError("No default given for %s" % field)
            #apply defaults
            if (not field in kwargs) and "default" in schema:
                self._calculated[field] = copy(schema["default"])
    def __getattr__(self,name):
        if name in self._kwargs:
            return self._kwargs.get(name)
        elif name in self._calculated:
            return self._calculated.get(name)
        else:
            raise AttributeError('Class %s has no attribute %s' %\
                (type(self),name))

    def as_dict(self,full=False):
        """
        Return the content of this instance as a dict.
        full=True includes also default and calculated values
        """
        if full:
            res = {}
            res.update(self._kwargs)
            res.update(self._calculated)
            return res
        else:
            return self._kwargs

class ReferenceBase(DataStruct):
    """
    A reference links to something in a store and can hold additional
    information. It can be dereferenced.
    """
    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)
    def dereference(self,store):
        """
        Dereference the reference and expand the referenced object and all
        fields in the reference into a dictionary.
        """
        raise NotImplementedError()

class ResourceReference(ReferenceBase):
    """
    A reference to a resource that is stored by res_id in a resource store.
    """
    _schema = load_schema(SCHEMA_DIR,'res_ref.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        ReferenceBase.__init__(self,**kwargs)
    def dereference(self,store):
        res = self.as_dict(full=True)
        res.update(store.get_res(self.res_id).as_dict())
        res["url"] = store.get_res_url(self.res_id)
        return res

class Resource(DataStruct):
    """
    Base class for data that is associated with a file.
    """
    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the res_id). This can be useful as a res_id or a
        part of if.

        Returns a string
        """
        if not "res_id" in kwargs:
            kwargs["res_id"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("res_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            check.update(key)
            check.update(val)
        return check.hexdigest()

class File(Resource):
    """
    File resource

    A File is a resource that has a filename as metadata.
    """
    _schema = load_schema(SCHEMA_DIR,'file.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        Resource.__init__(self,**kwargs)

class Image(Resource):
    """ Image resource

    An Image is a resource that has as metadata an alternative description
    and a filename extension indicating the format of the image.
    """
    _schema = load_schema(SCHEMA_DIR,'image.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        Resource.__init__(self,**kwargs)

class ObjectReference(ReferenceBase):
    """
    A reference to an object that is stored by its obj_id in an object store.

    A object can not be created and optional at the same time.
    """
    _schema = load_schema(SCHEMA_DIR,'obj_ref.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        ReferenceBase.__init__(self,**kwargs)
        assert not (self.created and self.optional)
    def dereference(self,store):
        res = self.as_dict(full=True)
        res.update(store.get_obj(self.obj_id).as_dict(full=True))
        res["images"] = [ref.dereference(store) for ref in res["images"]]
        return res

class Object(DataStruct):
    """
    An object is a physical object that is in some sense relevant to the
    assembly process. It can be either a part that is consumed in a step, a
    tool that isn't, or a result that is created in a step.
    """
    _schema = load_schema(SCHEMA_DIR,'object.json')
    _validator = jsonschema.Draft4Validator(
        _schema,
        types={'resref' : (ResourceReference,)}
    )

    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)
    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the obj_id). This can be useful as a obj_id or a
        part of if.

        Returns a string
        """
        if not "obj_id" in kwargs:
            kwargs["obj"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("obj_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            if key == "images":
                for img in val:
                    check.update(img.res_id)
            else:
                check.update(key)
                check.update(val)
        return check.hexdigest()

class Step(DataStruct):
    """
    One Step of the instructions
    """
    _schema = load_schema(SCHEMA_DIR,'step.json')
    _validator = jsonschema.Draft4Validator(
        _schema,
        types={
            "timedelta" : (timedelta,),
            "objref" : (ObjectReference,),
            "resref" : (ResourceReference,)
        }
    )

    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)

        for res in self.results.values():
            assert res.created

    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the step_id). This can be useful as a obj_id or a
        part of if.

        Returns a string
        """
        if not "step_id" in kwargs:
            kwargs["step_id"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("step_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            if key == "images":
                for alias,img in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(img.res_id)
            elif key == "files":
                for alias,att in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(att.res_id)
            elif key in ["parts","tools","results"]:
                for alias,obj in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(obj.obj_id)
            elif key in ["duration","waiting"]:
                check.update(str(val.total_seconds()))
            else:
                check.update(key)
                check.update(val)
        return check.hexdigest()

    def dereference(self,store):
        """
        Resolve all references and return content of step as a dict
        flattens namespaces to lists
        """
        res = self.as_dict(full=True)
        for nsp in ["parts","tools","results","images","files"]:
            res[nsp] = [ref.dereference(store) for ref in res[nsp].values()]
        return res

def graphviz_add_obj_edges(graph,s_id,objs,**kwargs):
    """
    add objects in the list objs to graph.

    kwargs:
    attr: dict of edge attributes
    opt: additional edge attributes for optional objects
    res: also add links to resources referenced by this object
    """
    for obj in objs:
        o_id = 'o_' + obj["obj_id"]
        if obj["optional"] and "opt" in kwargs:
            kwargs["attr"].update(kwargs["opt"])
        kwargs["attr"]["label"] = obj["quantity"]
        if obj["created"]:
            graph.add_edge(s_id,o_id,**kwargs["attr"])
        else:
            graph.add_edge(o_id,s_id,**kwargs["attr"])
        if kwargs["res"]:
            for img in obj["images"]:
                graph.add_edge('r_' + img["res_id"],o_id)

<commit_msg>fixed typo<commit_after>"""
This module defines common classes and interfaces for manual labour
"""

import json
import pkg_resources
import hashlib
from copy import copy
from os.path import join
from datetime import timedelta

import jsonschema

SCHEMA_DIR =  pkg_resources.resource_filename('manuallabour.core','schema')

def dereference_schema(schema_dir,schema):
    """
    Dereference JSON references.

    Only works for relative filesystem references like
    "$ref" : "sibling.json#/schemaname"
    and can not handle recursive references.
    """
    for key,val in schema.iteritems():
        if key == "$ref":
            fname,sname = val.split('#/')
            with open(join(schema_dir,fname)) as fid:
                ref_schema = json.loads(fid.read())
            schema.update(ref_schema[sname])
            schema.pop("$ref")
            return
        elif isinstance(val,dict):
            dereference_schema(schema_dir,val)
        elif isinstance(val,list):
            for sub in val:
                if isinstance(sub,dict):
                    dereference_schema(schema_dir,sub)

def load_schema(schema_dir,schema_name):
    """
    Load a jsonschema and dereferences JSON references by substitution.
    """
    with open(join(schema_dir,schema_name)) as fid:
        schema = json.loads(fid.read())
    dereference_schema(schema_dir,schema)
    return schema

class DataStruct(object):
    """
    A validating convenience wrapper around a dictionary. Serves as basis for
    many of the classes holding data.
    """
    _schema = None
    """JSON schema for the input of this class"""
    _validator = None
    """Validator for the schema of this class"""
    def __init__(self,**kwargs):
        self._validator.validate(kwargs)
        self._kwargs = kwargs
        self._calculated = {}
        for field, schema in self._schema["properties"].iteritems():
            #check for missing defaults
            if (not field in self._schema.get("required",[])) and \
                not "default" in schema:
                raise ValueError("No default given for %s" % field)
            #apply defaults
            if (not field in kwargs) and "default" in schema:
                self._calculated[field] = copy(schema["default"])
    def __getattr__(self,name):
        if name in self._kwargs:
            return self._kwargs.get(name)
        elif name in self._calculated:
            return self._calculated.get(name)
        else:
            raise AttributeError('Class %s has no attribute %s' %\
                (type(self),name))

    def as_dict(self,full=False):
        """
        Return the content of this instance as a dict.
        full=True includes also default and calculated values
        """
        if full:
            res = {}
            res.update(self._kwargs)
            res.update(self._calculated)
            return res
        else:
            return self._kwargs

class ReferenceBase(DataStruct):
    """
    A reference links to something in a store and can hold additional
    information. It can be dereferenced.
    """
    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)
    def dereference(self,store):
        """
        Dereference the reference and expand the referenced object and all
        fields in the reference into a dictionary.
        """
        raise NotImplementedError()

class ResourceReference(ReferenceBase):
    """
    A reference to a resource that is stored by res_id in a resource store.
    """
    _schema = load_schema(SCHEMA_DIR,'res_ref.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        ReferenceBase.__init__(self,**kwargs)
    def dereference(self,store):
        res = self.as_dict(full=True)
        res.update(store.get_res(self.res_id).as_dict())
        res["url"] = store.get_res_url(self.res_id)
        return res

class Resource(DataStruct):
    """
    Base class for data that is associated with a file.
    """
    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the res_id). This can be useful as a res_id or a
        part of if.

        Returns a string
        """
        if not "res_id" in kwargs:
            kwargs["res_id"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("res_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            check.update(key)
            check.update(val)
        return check.hexdigest()

class File(Resource):
    """
    File resource

    A File is a resource that has a filename as metadata.
    """
    _schema = load_schema(SCHEMA_DIR,'file.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        Resource.__init__(self,**kwargs)

class Image(Resource):
    """ Image resource

    An Image is a resource that has as metadata an alternative description
    and a filename extension indicating the format of the image.
    """
    _schema = load_schema(SCHEMA_DIR,'image.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        Resource.__init__(self,**kwargs)

class ObjectReference(ReferenceBase):
    """
    A reference to an object that is stored by its obj_id in an object store.

    A object can not be created and optional at the same time.
    """
    _schema = load_schema(SCHEMA_DIR,'obj_ref.json')
    _validator = jsonschema.Draft4Validator(_schema)

    def __init__(self,**kwargs):
        ReferenceBase.__init__(self,**kwargs)
        assert not (self.created and self.optional)
    def dereference(self,store):
        res = self.as_dict(full=True)
        res.update(store.get_obj(self.obj_id).as_dict(full=True))
        res["images"] = [ref.dereference(store) for ref in res["images"]]
        return res

class Object(DataStruct):
    """
    An object is a physical object that is in some sense relevant to the
    assembly process. It can be either a part that is consumed in a step, a
    tool that isn't, or a result that is created in a step.
    """
    _schema = load_schema(SCHEMA_DIR,'object.json')
    _validator = jsonschema.Draft4Validator(
        _schema,
        types={'resref' : (ResourceReference,)}
    )

    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)
    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the obj_id). This can be useful as a obj_id or a
        part of if.

        Returns a string
        """
        if not "obj_id" in kwargs:
            kwargs["obj_id"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("obj_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            if key == "images":
                for img in val:
                    check.update(img.res_id)
            else:
                check.update(key)
                check.update(val)
        return check.hexdigest()

class Step(DataStruct):
    """
    One Step of the instructions
    """
    _schema = load_schema(SCHEMA_DIR,'step.json')
    _validator = jsonschema.Draft4Validator(
        _schema,
        types={
            "timedelta" : (timedelta,),
            "objref" : (ObjectReference,),
            "resref" : (ResourceReference,)
        }
    )

    def __init__(self,**kwargs):
        DataStruct.__init__(self,**kwargs)

        for res in self.results.values():
            assert res.created

    @classmethod
    def calculate_checksum(cls,**kwargs):
        """
        Utility function for calculating a sha512 checksum over the keyword
        arguments (excluding the step_id). This can be useful as a obj_id or a
        part of if.

        Returns a string
        """
        if not "step_id" in kwargs:
            kwargs["step_id"] = "dummy"
        cls._validator.validate(kwargs)
        kwargs.pop("step_id")

        check = hashlib.sha512()
        #sort by key to get reproducible order
        for key,val in sorted(kwargs.iteritems(),key=lambda x: x[0]):
            if key == "images":
                for alias,img in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(img.res_id)
            elif key == "files":
                for alias,att in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(att.res_id)
            elif key in ["parts","tools","results"]:
                for alias,obj in sorted(val.iteritems(),key=lambda x: x[0]):
                    check.update(alias)
                    check.update(obj.obj_id)
            elif key in ["duration","waiting"]:
                check.update(str(val.total_seconds()))
            else:
                check.update(key)
                check.update(val)
        return check.hexdigest()

    def dereference(self,store):
        """
        Resolve all references and return content of step as a dict
        flattens namespaces to lists
        """
        res = self.as_dict(full=True)
        for nsp in ["parts","tools","results","images","files"]:
            res[nsp] = [ref.dereference(store) for ref in res[nsp].values()]
        return res

def graphviz_add_obj_edges(graph,s_id,objs,**kwargs):
    """
    add objects in the list objs to graph.

    kwargs:
    attr: dict of edge attributes
    opt: additional edge attributes for optional objects
    res: also add links to resources referenced by this object
    """
    for obj in objs:
        o_id = 'o_' + obj["obj_id"]
        if obj["optional"] and "opt" in kwargs:
            kwargs["attr"].update(kwargs["opt"])
        kwargs["attr"]["label"] = obj["quantity"]
        if obj["created"]:
            graph.add_edge(s_id,o_id,**kwargs["attr"])
        else:
            graph.add_edge(o_id,s_id,**kwargs["attr"])
        if kwargs["res"]:
            for img in obj["images"]:
                graph.add_edge('r_' + img["res_id"],o_id)

<|endoftext|>


<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst

import numpy as np

from numpy.testing import assert_allclose
import pytest
from astropy.utils import NumpyRNGContext
from astropy.nddata import StdDevUncertainty
from astropy import units as u

from ccdproc.core import (cosmicray_lacosmic, cosmicray_median,
                    background_deviation_box, background_deviation_filter)
from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func

DATA_SCALE = 5.3
NCRAYS = 30

def add_cosmicrays(data, scale, threshold, ncrays=NCRAYS):
    size = data.shape[0]
    with NumpyRNGContext(125):
        crrays = np.random.randint(0, size, size=(ncrays, 2))
        # use (threshold + 1) below to make sure cosmic ray is well above the
        # threshold no matter what the random number generator returns
        crflux = (10 * scale * np.random.random(NCRAYS) +
                  (threshold + 5) * scale)
        for i in range(ncrays):
            y, x = crrays[i]
            data.data[y, x] = crflux[i]

def test_cosmicray_lacosmic():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    data, crarr = cosmicray_lacosmic(ccd_data.data, sigclip=5)

    # check the number of cosmic rays detected
    # currently commented out while checking on issues
    # in astroscrappy
    # assert crarr.sum() == NCRAYS

def test_cosmicray_lacosmic_ccddata():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    nccd_data = cosmicray_lacosmic(ccd_data, sigclip=5)

    # check the number of cosmic rays detected
    # currently commented out while checking on issues
    # in astroscrappy
    # assert nccd_data.mask.sum() == NCRAYS

def test_cosmicray_lacosmic_check_data():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    with pytest.raises(TypeError):
        noise = DATA_SCALE * np.ones_like(ccd_data.data)
        cosmicray_lacosmic(10, noise)

@pytest.mark.parametrize('array_input', [True, False])
@pytest.mark.parametrize('gain_correct_data', [True, False])
def test_cosmicray_gain_correct(array_input, gain_correct_data):
    # Add regression check for #705 and for the new gain_correct
    # argument.
    # The issue is that cosmicray_lacosmic gain-corrects the
    # data and returns that gain corrected data. That is not the
    # intent...
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    # No units here on purpose.
    gain = 2.0
    # Don't really need to set this (6.5 is the default value) but want to
    # make lack of units explicit.
    readnoise = 6.5
    if array_input:
        new_data, cr_mask = cosmicray_lacosmic(ccd_data.data,
                                               gain=gain,
                                               gain_apply=gain_correct_data)
    else:
        new_ccd = cosmicray_lacosmic(ccd_data,
                                     gain=gain,
                                     gain_apply=gain_correct_data)
        new_data = new_ccd.data
        cr_mask = new_ccd.mask
    # Fill masked locations with 0 since there is no simple relationship
    # between the original value and the corrected value.
    orig_data = np.ma.array(ccd_data.data, mask=cr_mask).filled(0)
    new_data = np.ma.array(new_data.data, mask=cr_mask).filled(0)
    if gain_correct_data:
        gain_for_test = gain
    else:
        gain_for_test = 1.0

    np.testing.assert_allclose(gain_for_test * orig_data, new_data)

def test_cosmicray_lacosmic_accepts_quantity_gain():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    # The units below are the point of the test
    gain = 2.0 * u.electron / u.adu

    # Since gain and ccd_data have units, the readnoise should too.
    readnoise = 6.5 * u.electron
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True)

def test_cosmicray_lacosmic_accepts_quantity_readnoise():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    gain = 2.0 * u.electron / u.adu
    # The units below are the point of this test
    readnoise = 6.5 * u.electron
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True,
                                 readnoise=readnoise)

def test_cosmicray_lacosmic_detects_inconsistent_units():
    # This is intended to detect cases like a ccd with units
    # of adu, a readnoise in electrons and a gain in adu / electron.
    # That is not internally inconsistent.
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    ccd_data.unit = 'adu'
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    readnoise = 6.5 * u.electron

    # The units below are deliberately incorrect.
    gain = 2.0 * u.adu / u.electron
    with pytest.raises(ValueError) as e:
        cosmicray_lacosmic(ccd_data,
                           gain=gain,
                           gain_apply=True,
                           readnoise=readnoise)
    assert 'Inconsistent units' in str(e.value)

def test_cosmicray_lacosmic_warns_on_ccd_in_electrons(recwarn):
    # Check that an input ccd in electrons raises a warning.
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    # The unit below is important for the test; this unit on
    # input is supposed to raise an error.
    ccd_data.unit = u.electron
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    # No units here on purpose.
    gain = 2.0
    # Don't really need to set this (6.5 is the default value) but want to
    # make lack of units explicit.
    readnoise = 6.5
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True,
                                 readnoise=readnoise)
    assert len(recwarn) == 1
    assert "Image unit is electron" in str(recwarn.pop())

def test_cosmicray_median_check_data():
    with pytest.raises(TypeError):
        ndata, crarr = cosmicray_median(10, thresh=5, mbox=11,
                                        error_image=DATA_SCALE)

def test_cosmicray_median():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    ndata, crarr = cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                                    error_image=DATA_SCALE)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_ccddata():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    ccd_data.uncertainty = ccd_data.data*0.0+DATA_SCALE
    nccd = cosmicray_median(ccd_data, thresh=5, mbox=11,
                            error_image=None)

    # check the number of cosmic rays detected
    assert nccd.mask.sum() == NCRAYS

def test_cosmicray_median_masked():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    data = np.ma.masked_array(ccd_data.data, (ccd_data.data > -1e6))
    ndata, crarr = cosmicray_median(data, thresh=5, mbox=11,
                                    error_image=DATA_SCALE)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_background_None():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    data, crarr = cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                                   error_image=None)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_gbox():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    scale = DATA_SCALE  # yuck. Maybe use pytest.parametrize?
    threshold = 5
    add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS)
    error = ccd_data.data*0.0+DATA_SCALE
    data, crarr = cosmicray_median(ccd_data.data, error_image=error,
                                   thresh=5, mbox=11, rbox=0, gbox=5)
    data = np.ma.masked_array(data, crarr)
    assert crarr.sum() > NCRAYS
    assert abs(data.std() - scale) < 0.1

def test_cosmicray_median_rbox():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    scale = DATA_SCALE  # yuck. Maybe use pytest.parametrize?
    threshold = 5
    add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS)
    error = ccd_data.data*0.0+DATA_SCALE
    data, crarr = cosmicray_median(ccd_data.data, error_image=error,
                                   thresh=5, mbox=11, rbox=21, gbox=5)
    assert data[crarr].mean() < ccd_data.data[crarr].mean()
    assert crarr.sum() > NCRAYS

def test_cosmicray_median_background_deviation():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    with pytest.raises(TypeError):
        cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                         error_image='blank')

def test_background_deviation_box():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    bd = background_deviation_box(cd, 25)
    assert abs(bd.mean() - scale) < 0.10

def test_background_deviation_box_fail():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    with pytest.raises(ValueError):
        background_deviation_box(cd, 0.5)

def test_background_deviation_filter():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    bd = background_deviation_filter(cd, 25)
    assert abs(bd.mean() - scale) < 0.10

def test_background_deviation_filter_fail():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    with pytest.raises(ValueError):
        background_deviation_filter(cd, 0.5)
<commit_msg>Check for log message and change assert<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst

import numpy as np

from numpy.testing import assert_allclose
import pytest
from astropy.utils import NumpyRNGContext
from astropy.nddata import StdDevUncertainty
from astropy import units as u

from ccdproc.core import (cosmicray_lacosmic, cosmicray_median,
                    background_deviation_box, background_deviation_filter)
from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func

DATA_SCALE = 5.3
NCRAYS = 30

def add_cosmicrays(data, scale, threshold, ncrays=NCRAYS):
    size = data.shape[0]
    with NumpyRNGContext(125):
        crrays = np.random.randint(0, size, size=(ncrays, 2))
        # use (threshold + 1) below to make sure cosmic ray is well above the
        # threshold no matter what the random number generator returns
        crflux = (10 * scale * np.random.random(NCRAYS) +
                  (threshold + 5) * scale)
        for i in range(ncrays):
            y, x = crrays[i]
            data.data[y, x] = crflux[i]

def test_cosmicray_lacosmic():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    data, crarr = cosmicray_lacosmic(ccd_data.data, sigclip=5)

    # check the number of cosmic rays detected
    # currently commented out while checking on issues
    # in astroscrappy
    # assert crarr.sum() == NCRAYS

def test_cosmicray_lacosmic_ccddata():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    nccd_data = cosmicray_lacosmic(ccd_data, sigclip=5)

    # check the number of cosmic rays detected
    # currently commented out while checking on issues
    # in astroscrappy
    # assert nccd_data.mask.sum() == NCRAYS

def test_cosmicray_lacosmic_check_data():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    with pytest.raises(TypeError):
        noise = DATA_SCALE * np.ones_like(ccd_data.data)
        cosmicray_lacosmic(10, noise)

@pytest.mark.parametrize('array_input', [True, False])
@pytest.mark.parametrize('gain_correct_data', [True, False])
def test_cosmicray_gain_correct(array_input, gain_correct_data):
    # Add regression check for #705 and for the new gain_correct
    # argument.
    # The issue is that cosmicray_lacosmic gain-corrects the
    # data and returns that gain corrected data. That is not the
    # intent...
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    # No units here on purpose.
    gain = 2.0
    # Don't really need to set this (6.5 is the default value) but want to
    # make lack of units explicit.
    readnoise = 6.5
    if array_input:
        new_data, cr_mask = cosmicray_lacosmic(ccd_data.data,
                                               gain=gain,
                                               gain_apply=gain_correct_data)
    else:
        new_ccd = cosmicray_lacosmic(ccd_data,
                                     gain=gain,
                                     gain_apply=gain_correct_data)
        new_data = new_ccd.data
        cr_mask = new_ccd.mask
    # Fill masked locations with 0 since there is no simple relationship
    # between the original value and the corrected value.
    orig_data = np.ma.array(ccd_data.data, mask=cr_mask).filled(0)
    new_data = np.ma.array(new_data.data, mask=cr_mask).filled(0)
    if gain_correct_data:
        gain_for_test = gain
    else:
        gain_for_test = 1.0

    np.testing.assert_allclose(gain_for_test * orig_data, new_data)

def test_cosmicray_lacosmic_accepts_quantity_gain():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    # The units below are the point of the test
    gain = 2.0 * u.electron / u.adu

    # Since gain and ccd_data have units, the readnoise should too.
    readnoise = 6.5 * u.electron
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True)

def test_cosmicray_lacosmic_accepts_quantity_readnoise():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    gain = 2.0 * u.electron / u.adu
    # The units below are the point of this test
    readnoise = 6.5 * u.electron
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True,
                                 readnoise=readnoise)

def test_cosmicray_lacosmic_detects_inconsistent_units():
    # This is intended to detect cases like a ccd with units
    # of adu, a readnoise in electrons and a gain in adu / electron.
    # That is not internally inconsistent.
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    ccd_data.unit = 'adu'
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    readnoise = 6.5 * u.electron

    # The units below are deliberately incorrect.
    gain = 2.0 * u.adu / u.electron
    with pytest.raises(ValueError) as e:
        cosmicray_lacosmic(ccd_data,
                           gain=gain,
                           gain_apply=True,
                           readnoise=readnoise)
    assert 'Inconsistent units' in str(e.value)

def test_cosmicray_lacosmic_warns_on_ccd_in_electrons(recwarn, caplog):
    # Check that an input ccd in electrons raises a warning.
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    # The unit below is important for the test; this unit on
    # input is supposed to raise an error.
    ccd_data.unit = u.electron
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    noise = DATA_SCALE * np.ones_like(ccd_data.data)
    ccd_data.uncertainty = noise
    assert "assuming it is a StdDevUncertainty" in caplog.text

    # No units here on purpose.
    gain = 2.0
    # Don't really need to set this (6.5 is the default value) but want to
    # make lack of units explicit.
    readnoise = 6.5
    new_ccd = cosmicray_lacosmic(ccd_data,
                                 gain=gain,
                                 gain_apply=True,
                                 readnoise=readnoise)
    assert len(recwarn) == 2
    assert "Image unit is electron" in str(recwarn.pop())

def test_cosmicray_median_check_data():
    with pytest.raises(TypeError):
        ndata, crarr = cosmicray_median(10, thresh=5, mbox=11,
                                        error_image=DATA_SCALE)

def test_cosmicray_median():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    ndata, crarr = cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                                    error_image=DATA_SCALE)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_ccddata():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    ccd_data.uncertainty = ccd_data.data*0.0+DATA_SCALE
    nccd = cosmicray_median(ccd_data, thresh=5, mbox=11,
                            error_image=None)

    # check the number of cosmic rays detected
    assert nccd.mask.sum() == NCRAYS

def test_cosmicray_median_masked():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    data = np.ma.masked_array(ccd_data.data, (ccd_data.data > -1e6))
    ndata, crarr = cosmicray_median(data, thresh=5, mbox=11,
                                    error_image=DATA_SCALE)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_background_None():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    threshold = 5
    add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS)
    data, crarr = cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                                   error_image=None)

    # check the number of cosmic rays detected
    assert crarr.sum() == NCRAYS

def test_cosmicray_median_gbox():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    scale = DATA_SCALE  # yuck. Maybe use pytest.parametrize?
    threshold = 5
    add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS)
    error = ccd_data.data*0.0+DATA_SCALE
    data, crarr = cosmicray_median(ccd_data.data, error_image=error,
                                   thresh=5, mbox=11, rbox=0, gbox=5)
    data = np.ma.masked_array(data, crarr)
    assert crarr.sum() > NCRAYS
    assert abs(data.std() - scale) < 0.1

def test_cosmicray_median_rbox():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    scale = DATA_SCALE  # yuck. Maybe use pytest.parametrize?
    threshold = 5
    add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS)
    error = ccd_data.data*0.0+DATA_SCALE
    data, crarr = cosmicray_median(ccd_data.data, error_image=error,
                                   thresh=5, mbox=11, rbox=21, gbox=5)
    assert data[crarr].mean() < ccd_data.data[crarr].mean()
    assert crarr.sum() > NCRAYS

def test_cosmicray_median_background_deviation():
    ccd_data = ccd_data_func(data_scale=DATA_SCALE)
    with pytest.raises(TypeError):
        cosmicray_median(ccd_data.data, thresh=5, mbox=11,
                         error_image='blank')

def test_background_deviation_box():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    bd = background_deviation_box(cd, 25)
    assert abs(bd.mean() - scale) < 0.10

def test_background_deviation_box_fail():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    with pytest.raises(ValueError):
        background_deviation_box(cd, 0.5)

def test_background_deviation_filter():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    bd = background_deviation_filter(cd, 25)
    assert abs(bd.mean() - scale) < 0.10

def test_background_deviation_filter_fail():
    with NumpyRNGContext(123):
        scale = 5.3
        cd = np.random.normal(loc=0, size=(100, 100), scale=scale)
    with pytest.raises(ValueError):
        background_deviation_filter(cd, 0.5)
<|endoftext|>


<commit_before>"""
Sensible Heat Storage - Fully Mixed tank
"""

from __future__ import division
import numpy as np
from scipy.integrate import odeint
import math

__author__ = "Shanshan Hsieh"
__copyright__ = "Copyright 2016, Architecture and Building Systems - ETH Zurich"
__credits__ = ["ShanShan Hsieh"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daren Thomas"
__email__ = "cea@arch.ethz.ch"
__status__ = "Production"

def calc_Qww_ls_st(ta, te, Tww_st, V, Qww, Qww_ls_r, Qww_ls_nr, gv):
    """
    This algorithm calculates the heat flows within a fully mixed water storage tank.
    Heat flows include sensible heat loss to the environment (ql), heat charged into the tank (qc),
    and heat discharged from the tank (qd).

    :param Tww_st: tank temperature in [C]
    :param Tww_setpoint: DHW temperature set point in [C]
    :param ta: room temperature in [C]
    :param Bf: the coefficient of reduction in transmittance for surfaces in contact with the ground according to
    values of SIA 380/1
    :param te: ambient temperature in [C]
    :param V: DHW tank size in [m3]
    :param Qww: DHW demand in [W]
    :param Qww_ls_r: recoverable loss in distribution in [W]
    :param Qww_ls_nr: non-recoverable loss in distribution in [W]
    :param Utank: dhw tank insulation heat transfer coefficient in [W/m2-K], value taken from SIA 385. global variable.
    :param AR: tank height aspect ratio, H=(4*V*AR^2/pi)^(1/3), value taken from commercial tank geometry (jenni.ch).
    :param gv: globalvar.py

    :type Tww_st: float
    :type Tww_setpoint: float
    :type ta: float
    :type Bf: float
    :type te: float
    :type V: float
    :type Qww: float
    :type Qww_ls_nr: float
    :type Utank: float
    :type AR: float

    :return ql: storage sensible heat loss in [W].
    :return qd: heat discharged from the tank in [W], including dhw heating demand and distribution heat loss.
    :return qc: heat charged into the tank in [W].
    :rtype ql: float
    :rtype qd: float
    :rtype qc: float
    """
    tamb = ta - gv.Bf * (ta - te)         # Calculate tamb in basement according to EN

    h = ( 4 * V * gv.AR ** 2 / math.pi ) ** ( 1.0 / 3.0 )     # tank height in [m], derived from tank Aspect Ratio(AR)
    r = ( V / ( math.pi * h ) ) ** ( 1.0 / 2.0 )         # tank radius in [m], assuming tank shape is cylinder
    Atank = 2 * math.pi * r ** 2 + 2 * math.pi * r * h      # tank surface area in [m2].
    ql = gv.Utank * Atank * ( Tww_st - tamb )       # tank heat loss to the room in [W]
    qd = Qww + Qww_ls_r + Qww_ls_nr
    if Qww <= 0:
        qc = 0
    else:
        qc = qd + ql + gv.Pwater * V * gv.Cpw * ( gv.Tww_setpoint - Tww_st ) / 3.6
    return ql, qd, qc

def ode(y, t, ql, qd, qc, Pwater, Cpw, Vtank):
    """
    This algorithm describe the energy balance of the dhw tank with a differential equation.

    :param y: storage sensible temperature in K.
    :param t: time steps.
    :param ql: storage tank sensible heat loss in W.
    :param qd: heat discharged from the tank in W.
    :param qc: heat charged into the tank in W.
    :type y: float
    :type t: float
    :type ql: float
    :type qd: float
    :type qc: float

    :return dydt: change in temperature at each time step.
    :type dydt: float
    """
    dydt = (qc - ql - qd) / (Pwater * Vtank * Cpw)
    return dydt

def solve_ode_storage(Tww_st_0, ql, qd, qc, Vtank, gv):
    """
    This algorithm solves the differential equation, ode.

    :param Tww_st_0: initial tank temperature in [C]
    :param ql: storage tank sensible heat loss in W.
    :param qd: heat discharged from the tank in W.
    :param qc: heat charged into the tank in W.
    :param Vtank: DHW tank size in [m3]
    :param gv: globalvar.py

    :type Tww_st_0: float
    :type ql: float
    :type qd: float
    :type qc: float
    :type Vtank: float

    :returns y[1]: solution of the ode
    :rtype y[1]: float
    """
    t = np.linspace(0,1,2)
    y = odeint(ode, Tww_st_0, t, args = (ql, qd, qc, gv.Pwater, gv.Cpw, Vtank))

    return y[1]

# use the optimized (numba_cc) versions of the ode function in this module if available
try:
    # import Numba AOT versions of the functions above, overwriting them
    from storagetank_cc import (ode)
except ImportError:
    # fall back to using the python version
    print('failed to import from storagetank_cc.pyd, falling back to pure python functions')
    pass<commit_msg>fixed some formatting<commit_after>"""
Sensible Heat Storage - Fully Mixed tank
"""

from __future__ import division
import numpy as np
from scipy.integrate import odeint
import math

__author__ = "Shanshan Hsieh"
__copyright__ = "Copyright 2016, Architecture and Building Systems - ETH Zurich"
__credits__ = ["ShanShan Hsieh"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daren Thomas"
__email__ = "cea@arch.ethz.ch"
__status__ = "Production"

def calc_Qww_ls_st(ta, te, Tww_st, V, Qww, Qww_ls_r, Qww_ls_nr, gv):
    """
    This algorithm calculates the heat flows within a fully mixed water storage tank.
    Heat flows include sensible heat loss to the environment (ql), heat charged into the tank (qc),
    and heat discharged from the tank (qd).

    :param Tww_st: tank temperature in [C]
    :param Tww_setpoint: DHW temperature set point in [C]
    :param ta: room temperature in [C]
    :param Bf: the coefficient of reduction in transmittance for surfaces in contact with the ground according to
    values of SIA 380/1
    :param te: ambient temperature in [C]
    :param V: DHW tank size in [m3]
    :param Qww: DHW demand in [W]
    :param Qww_ls_r: recoverable loss in distribution in [W]
    :param Qww_ls_nr: non-recoverable loss in distribution in [W]
    :param Utank: dhw tank insulation heat transfer coefficient in [W/m2-K], value taken from SIA 385. global variable.
    :param AR: tank height aspect ratio, H=(4*V*AR^2/pi)^(1/3), value taken from commercial tank geometry (jenni.ch).
    :param gv: globalvar.py

    :type Tww_st: float
    :type Tww_setpoint: float
    :type ta: float
    :type Bf: float
    :type te: float
    :type V: float
    :type Qww: float
    :type Qww_ls_nr: float
    :type Utank: float
    :type AR: float

    :return ql: storage sensible heat loss in [W].
    :return qd: heat discharged from the tank in [W], including dhw heating demand and distribution heat loss.
    :return qc: heat charged into the tank in [W].
    :rtype ql: float
    :rtype qd: float
    :rtype qc: float
    """
    tamb = ta - gv.Bf * (ta - te)         # Calculate tamb in basement according to EN

    h = (4 * V * gv.AR ** 2 / math.pi) ** (1.0 / 3.0)  # tank height in [m], derived from tank Aspect Ratio(AR)
    r = (V / (math.pi * h)) ** (1.0 / 2.0)  # tank radius in [m], assuming tank shape is cylinder
    Atank = 2 * math.pi * r ** 2 + 2 * math.pi * r * h  # tank surface area in [m2].
    ql = gv.Utank * Atank * (Tww_st - tamb)  # tank heat loss to the room in [W]
    qd = Qww + Qww_ls_r + Qww_ls_nr
    if Qww <= 0:
        qc = 0
    else:
        qc = qd + ql + gv.Pwater * V * gv.Cpw * (gv.Tww_setpoint - Tww_st) / 3.6
    return ql, qd, qc

def ode(y, t, ql, qd, qc, Pwater, Cpw, Vtank):
    """
    This algorithm describe the energy balance of the dhw tank with a differential equation.

    :param y: storage sensible temperature in K.
    :param t: time steps.
    :param ql: storage tank sensible heat loss in W.
    :param qd: heat discharged from the tank in W.
    :param qc: heat charged into the tank in W.
    :type y: float
    :type t: float
    :type ql: float
    :type qd: float
    :type qc: float

    :return dydt: change in temperature at each time step.
    :type dydt: float
    """
    dydt = (qc - ql - qd) / (Pwater * Vtank * Cpw)
    return dydt

def solve_ode_storage(Tww_st_0, ql, qd, qc, Vtank, gv):
    """
    This algorithm solves the differential equation, ode.

    :param Tww_st_0: initial tank temperature in [C]
    :param ql: storage tank sensible heat loss in W.
    :param qd: heat discharged from the tank in W.
    :param qc: heat charged into the tank in W.
    :param Vtank: DHW tank size in [m3]
    :param gv: globalvar.py

    :type Tww_st_0: float
    :type ql: float
    :type qd: float
    :type qc: float
    :type Vtank: float

    :returns y[1]: solution of the ode
    :rtype y[1]: float
    """
    t = np.linspace(0,1,2)
    y = odeint(ode, Tww_st_0, t, args = (ql, qd, qc, gv.Pwater, gv.Cpw, Vtank))

    return y[1]

# use the optimized (numba_cc) versions of the ode function in this module if available
try:
    # import Numba AOT versions of the functions above, overwriting them
    from storagetank_cc import (ode)
except ImportError:
    # fall back to using the python version
    print('failed to import from storagetank_cc.pyd, falling back to pure python functions')
    pass<|endoftext|>


<commit_before>'''
Created on 14.06.2011

@author: michi
'''
import datetime

from PyQt4.QtCore import QAbstractTableModel, QModelIndex, Qt, QVariant, QString

from sqlalchemy.orm import object_mapper, ColumnProperty, RelationshipProperty
from sqlalchemy.util import NamedTuple
from ems import qt4
from ems.thirdparty.odict import OrderedDict

class SAOrmSearchModel(QAbstractTableModel):
    def __init__(self,session, queriedObject, querybuilder, query=None, columns = []):
        super(SAOrmSearchModel, self).__init__()
        self._session = session
        self._queriedObject = queriedObject
        self._resultCache = {}
        self._columns = columns
        if not len(self._columns):
            self._columns = self.possibleColumns
        self._mapper = None
        self._ormProperties = None
        self._flagsCache = {}
        self._queryBuilder = querybuilder
        if query is None:
            query = self._queryBuilder.getQuery(self._session,
                                                propertySelection=self._columns)
        self._query = query
        self._headerNameCache = {}
        self._defaultColumns = []

        self._columnName2Index = self._buildReversedColumnLookup(columns)
        self._dirty = True

    def getQuery(self):
        return self._query

    def setQuery(self, query):
        self._query = query
        self._dirty = True
        self.perform()

    query = property(getQuery, setQuery)

    @property
    def mapper(self):
        if self._mapper is None:
            self._mapper = object_mapper(self._queriedObject())
        return self._mapper

    @property
    def ormProperties(self):
        if self._ormProperties is None:
            self._ormProperties = OrderedDict()
            for propertyName in self._queryBuilder.propertyNamesDecorated:
                self._ormProperties[propertyName] = \
                    self._queryBuilder.properties[propertyName]
                #self._ormProperties.append(property)
        return self._ormProperties

    @property
    def possibleColumns(self):
        if not len(self._defaultColumns):
            self._defaultColumns = self.__buildDefaultColumns()
        return self._defaultColumns

    def __buildDefaultColumns(self):
        columns = []
        for property in self.ormProperties.keys():
            columns.append(property)
        return columns

    def _buildReversedColumnLookup(self, columns):
        i = 0
        reversed = {}
        for column in columns:
            reversed[str(column)] = i
            i += 1
        return reversed

    def getColumns(self):
        return self._columns

    def setColumns(self, cols):
        self._columns = cols
        self._dirty = True
        self.perform()

    columns = property(getColumns, setColumns)

    @property
    def session(self):
        return self._session

    def rowCount(self, index=QModelIndex()):
        self.perform()
        return len(self._resultCache)

    def columnCount(self, index=QModelIndex()):
        self.perform()
        return len(self._columns)

    def getPropertyNameByIndex(self, index):
        return self._columns[index]

    def getIndexByPropertyName(self, name):
        return self._columnName2Index[name]

    def extractValue(self, index, propertyName):

        currentObj = self._resultCache[index.row()]
        if hasattr(currentObj, propertyName):
            return currentObj.__getattribute__(propertyName)
        else:
            if propertyName.find('.'):
                stack = propertyName.split('.')
                value = self._extractValue(currentObj, stack)
                if value:
                    return value

        return "*Nichts*"

    def _extractValue(self, obj, pathStack):

        if(hasattr(obj, pathStack[0])):
            if len(pathStack) < 2:
                return obj.__getattribute__(pathStack[0])
            nextObj = obj.__getattribute__(pathStack[0])
            pathStack.pop(0)
            return self._extractValue(nextObj, pathStack)

    def data(self, index, role=Qt.DisplayRole):
        self.perform()
        columnName = self.getPropertyNameByIndex(index.column())
        #return QVariant()
        if not index.isValid() or \
           not (0 <= index.row() < self.rowCount()):
            return QVariant()
        if role in (Qt.DisplayRole, Qt.EditRole):
            #print self._resultCache[index.row()]
            #value = self._resultCache[index.row()].__getattribute__(columnName)
            #return QVariant("Test")
            value = self.extractValue(index, columnName)
            #print value
#            if self._queriedObject.__name__ == 'Gruppe':
#                print "row:%s col:%s role:%s value:%s" % (index.row(), index.column(), role, value)
            if isinstance(value, basestring):
                return QVariant(unicode(value))
            elif hasattr(value.__class__,'__ormDecorator__'):
                return QVariant(value.__class__.__ormDecorator__().getReprasentiveString(value))
            elif isinstance(value, datetime.datetime):
                return QVariant(value.strftime("%c"))
            return QVariant(value)
        if role == qt4.ColumnNameRole:
            return QVariant(unicode(self._queryBuilder.currentColumnList[index.column()]))
        return QVariant()

    def getObject(self, row):
        if self._resultCache.has_key(row):
            return self._resultCache[row]

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
                return QVariant(int(Qt.AlignLeft|Qt.AlignVCenter))
            return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            columnName = unicode(self.getPropertyNameByIndex(section))
            name = self.getPropertyFriendlyName(columnName)

            return QVariant(name)
        return QVariant(int(section + 1))

    def getPropertyFriendlyName(self, propertyPath):
        if not self._headerNameCache.has_key(propertyPath):
            fieldName = propertyPath.split('.')[-1:][0]
            #print "%s %s" % (columnName, columnName.split('.')[-1:][0])
            try:
                dec = self._queryBuilder.propertyName2Class[propertyPath].__ormDecorator__()
                name = dec.getPropertyFriendlyName(fieldName)

            except KeyError:
                name = fieldName
            self._headerNameCache[propertyPath] = QString.fromUtf8(name)

        return self._headerNameCache[propertyPath]

    def isPrimaryKey(self, index):
        self._flagsCache

        print

#    def flags(self, index):
#
#        propertyName = self.getPropertyNameByIndex(index.column())
#        if not self._flagsCache.has_key(propertyName):
#            isPk = False
#            if isinstance(self.ormProperties[propertyName], ColumnProperty):
#                for col in self.ormProperties[propertyName].columns:
#                    if col.primary_key:
#                        isPk = True
#                if not isPk:
#                    self._flagsCache[propertyName] = Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
#                else:
#                    self._flagsCache[propertyName] = Qt.ItemIsSelectable | Qt.ItemIsEnabled
#            else:
#                self._flagsCache[propertyName] = Qt.ItemIsSelectable | Qt.ItemIsEnabled
#
#        return self._flagsCache[propertyName]

    def isDataChanged(self):
        return self._session.dirty

    def perform(self):

        if not self._dirty:
            return
        #self.beginResetModel()
        #print "%s : I actually perform" % self._queriedObject
        #print self._session.get_bind(self._queriedObject)
        i = 0

        self.beginResetModel()

        self._resultCache.clear()
        for obj in self._query.all():

            if isinstance(obj, NamedTuple):
                self._resultCache[i] = obj[0]
            else:
                self._resultCache[i] = obj
            i += 1
        self._dirty = False
        self.endResetModel()<commit_msg>* Fehler behoben, der dazu führte dass bei manueller Query die Properties nicht gezogen wurden<commit_after>'''
Created on 14.06.2011

@author: michi
'''
import datetime

from PyQt4.QtCore import QAbstractTableModel, QModelIndex, Qt, QVariant, QString

from sqlalchemy.orm import object_mapper, ColumnProperty, RelationshipProperty
from sqlalchemy.util import NamedTuple
from ems import qt4
from ems.thirdparty.odict import OrderedDict

class SAOrmSearchModel(QAbstractTableModel):
    def __init__(self,session, queriedObject, querybuilder, query=None, columns = []):
        super(SAOrmSearchModel, self).__init__()
        self._session = session
        self._queriedObject = queriedObject
        self._resultCache = {}
        self._columns = columns
        if not len(self._columns):
            self._columns = self.possibleColumns
        self._mapper = None
        self._ormProperties = None
        self._flagsCache = {}
        self._queryBuilder = querybuilder
        if query is None:
            query = self._queryBuilder.getQuery(self._session,
                                                propertySelection=self._columns)
        else:
            self._queryBuilder.propertyNames
        self._query = query
        self._headerNameCache = {}
        self._defaultColumns = []

        self._columnName2Index = self._buildReversedColumnLookup(columns)
        self._dirty = True

    def getQuery(self):
        return self._query

    def setQuery(self, query):
        self._query = query
        self._dirty = True
        self.perform()

    query = property(getQuery, setQuery)

    @property
    def mapper(self):
        if self._mapper is None:
            self._mapper = object_mapper(self._queriedObject())
        return self._mapper

    @property
    def ormProperties(self):
        if self._ormProperties is None:
            self._ormProperties = OrderedDict()
            for propertyName in self._queryBuilder.propertyNamesDecorated:
                self._ormProperties[propertyName] = \
                    self._queryBuilder.properties[propertyName]
                #self._ormProperties.append(property)
        return self._ormProperties

    @property
    def possibleColumns(self):
        if not len(self._defaultColumns):
            self._defaultColumns = self.__buildDefaultColumns()
        return self._defaultColumns

    def __buildDefaultColumns(self):
        columns = []
        for property in self.ormProperties.keys():
            columns.append(property)
        return columns

    def _buildReversedColumnLookup(self, columns):
        i = 0
        reversed = {}
        for column in columns:
            reversed[str(column)] = i
            i += 1
        return reversed

    def getColumns(self):
        return self._columns

    def setColumns(self, cols):
        self._columns = cols
        self._dirty = True
        self.perform()

    columns = property(getColumns, setColumns)

    @property
    def session(self):
        return self._session

    def rowCount(self, index=QModelIndex()):
        self.perform()
        return len(self._resultCache)

    def columnCount(self, index=QModelIndex()):
        self.perform()
        return len(self._columns)

    def getPropertyNameByIndex(self, index):
        return self._columns[index]

    def getIndexByPropertyName(self, name):
        return self._columnName2Index[name]

    def extractValue(self, index, propertyName):

        currentObj = self._resultCache[index.row()]
        if hasattr(currentObj, propertyName):
            return currentObj.__getattribute__(propertyName)
        else:
            if propertyName.find('.'):
                stack = propertyName.split('.')
                value = self._extractValue(currentObj, stack)
                if value:
                    return value

        return "*Nichts*"

    def _extractValue(self, obj, pathStack):

        if(hasattr(obj, pathStack[0])):
            if len(pathStack) < 2:
                return obj.__getattribute__(pathStack[0])
            nextObj = obj.__getattribute__(pathStack[0])
            pathStack.pop(0)
            return self._extractValue(nextObj, pathStack)

    def data(self, index, role=Qt.DisplayRole):
        self.perform()
        columnName = self.getPropertyNameByIndex(index.column())
        #return QVariant()
        if not index.isValid() or \
           not (0 <= index.row() < self.rowCount()):
            return QVariant()
        if role in (Qt.DisplayRole, Qt.EditRole):
            #print self._resultCache[index.row()]
            #value = self._resultCache[index.row()].__getattribute__(columnName)
            #return QVariant("Test")
            value = self.extractValue(index, columnName)
            #print value
#            if self._queriedObject.__name__ == 'Gruppe':
#                print "row:%s col:%s role:%s value:%s" % (index.row(), index.column(), role, value)
            if isinstance(value, basestring):
                return QVariant(unicode(value))
            elif hasattr(value.__class__,'__ormDecorator__'):
                return QVariant(value.__class__.__ormDecorator__().getReprasentiveString(value))
            elif isinstance(value, datetime.datetime):
                return QVariant(value.strftime("%c"))
            return QVariant(value)
        if role == qt4.ColumnNameRole:
            return QVariant(unicode(self._queryBuilder.currentColumnList[index.column()]))
        return QVariant()

    def getObject(self, row):
        if self._resultCache.has_key(row):
            return self._resultCache[row]

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
                return QVariant(int(Qt.AlignLeft|Qt.AlignVCenter))
            return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            columnName = unicode(self.getPropertyNameByIndex(section))
            name = self.getPropertyFriendlyName(columnName)

            return QVariant(name)
        return QVariant(int(section + 1))

    def getPropertyFriendlyName(self, propertyPath):
        if not self._headerNameCache.has_key(propertyPath):
            fieldName = propertyPath.split('.')[-1:][0]
            #print "%s %s" % (columnName, columnName.split('.')[-1:][0])
            try:
                dec = self._queryBuilder.propertyName2Class[propertyPath].__ormDecorator__()
                name = dec.getPropertyFriendlyName(fieldName)

            except KeyError:
                name = fieldName
            self._headerNameCache[propertyPath] = QString.fromUtf8(name)

        return self._headerNameCache[propertyPath]

    def isPrimaryKey(self, index):
        self._flagsCache

        print

#    def flags(self, index):
#
#        propertyName = self.getPropertyNameByIndex(index.column())
#        if not self._flagsCache.has_key(propertyName):
#            isPk = False
#            if isinstance(self.ormProperties[propertyName], ColumnProperty):
#                for col in self.ormProperties[propertyName].columns:
#                    if col.primary_key:
#                        isPk = True
#                if not isPk:
#                    self._flagsCache[propertyName] = Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
#                else:
#                    self._flagsCache[propertyName] = Qt.ItemIsSelectable | Qt.ItemIsEnabled
#            else:
#                self._flagsCache[propertyName] = Qt.ItemIsSelectable | Qt.ItemIsEnabled
#
#        return self._flagsCache[propertyName]

    def isDataChanged(self):
        return self._session.dirty

    def perform(self):

        if not self._dirty:
            return
        #self.beginResetModel()
        #print "%s : I actually perform" % self._queriedObject
        #print self._session.get_bind(self._queriedObject)
        i = 0

        self.beginResetModel()

        self._resultCache.clear()
        for obj in self._query.all():

            if isinstance(obj, NamedTuple):
                self._resultCache[i] = obj[0]
            else:
                self._resultCache[i] = obj
            i += 1
        self._dirty = False
        self.endResetModel()<|endoftext|>