<commit_before>import os
import vtk
from PyQt5 import QtCore, QtWidgets
import xml.etree.ElementTree as ET
import gui.howto
from gui.vtkwidget import VtkWidget
from gui.progress import ProgressBarFrame
from gui.datapanel import DataPanel
from gui.propspanel import PropsPanel
from IO.project import ProjectIO

#==================================================================================================
# MainWindow ======================================================================================
#==================================================================================================
class MainWindow(QtWidgets.QMainWindow):
  def __init__(self, qt_app, controller):
    QtWidgets.QMainWindow.__init__(self)

    # This guy is used by several classes to indicate the progress of the heavy work
    self.__progress_bar = ProgressBarFrame(self, qt_app)

    self.__controller = controller
    self.__data_container = controller.data_container
    self.__brain = controller.brain

    # This guy handles the file/project IO
    self.__project_io = ProjectIO(self.__progress_bar)

    self.__add_menus()
    self.__setup_main_frame()

    # Load some info from a config file (like the folder where the user saved her last project)
    self.__config_file_name = "./brainvispy_config.xml"
    self.__load_config_file()

    # Change the title of the main window
    self.setWindowTitle("BrainVisPy")
    # Make sure that the main window appears (maximized) on the primary screen
    desktop_widget = QtWidgets.QDesktopWidget()
    rect = desktop_widget.availableGeometry(desktop_widget.primaryScreen())
    self.move(rect.x(), rect.y())
    self.showMaximized()

  def __add_menus(self):
    # Menu FILE
    # Open project
    open_project_action = QtWidgets.QAction('Open project', self)
    open_project_action.setShortcut('Ctrl+O')
    open_project_action.triggered.connect(self.__on_open_project)
    # Load file(s)
    load_files_action = QtWidgets.QAction('Load file(s)', self)
    load_files_action.setShortcut('Ctrl+L')
    load_files_action.triggered.connect(self.__on_load_files)
    # Load folder
    load_folder_action = QtWidgets.QAction('Import folder', self)
    load_folder_action.setShortcut('Ctrl+I')
    load_folder_action.triggered.connect(self.__on_import_folder)
    # Save project
    save_project_action = QtWidgets.QAction('Save project', self)
    save_project_action.setShortcut('Ctrl+S')
    save_project_action.triggered.connect(self.__on_save_project)
    # Save as project
    save_project_as_action = QtWidgets.QAction('Save project as', self)
    save_project_as_action.setShortcut('Ctrl+Shift+S')
    save_project_as_action.triggered.connect(self.__on_save_project_as)
    # Quit
    quit_action = QtWidgets.QAction('Quit', self)
    quit_action.setShortcut('Ctrl+Q')
    quit_action.triggered.connect(self.close)

    # HOW TO
    # Create neurons
    howto_create_neurons = QtWidgets.QAction('Create neurons', self)
    howto_create_neurons.triggered.connect(self.__on_howto_create_neurons)
    # Adjust neuron parameters
    howto_adjust_neuron_parameters = QtWidgets.QAction('Adjust neuron parameters', self)
    howto_adjust_neuron_parameters.triggered.connect(self.__on_howto_adjust_neuron_parameters)
    # Connect neurons
    howto_connect_neurons = QtWidgets.QAction('Connect neurons', self)
    howto_connect_neurons.triggered.connect(self.__on_howto_connect_neurons)
    # Adjust connection parameters
    howto_adjust_connection_parameters = QtWidgets.QAction('Adjust connection parameters', self)
    howto_adjust_connection_parameters.triggered.connect(self.__on_howto_adjust_connection_parameters)

    # Add everything to the menu bar
    # FILE
    file_menu = self.menuBar().addMenu("FILE")
    file_menu.addAction(open_project_action)
    file_menu.addAction(save_project_action)
    file_menu.addAction(save_project_as_action)
    file_menu.addSeparator()
    file_menu.addAction(load_files_action)
    file_menu.addAction(load_folder_action)
    file_menu.addSeparator()
    file_menu.addAction(quit_action)
    # HOWTO
    howto_menu = self.menuBar().addMenu("HOW TO")
    howto_menu.addAction(howto_create_neurons)
    howto_menu.addAction(howto_adjust_neuron_parameters)
    howto_menu.addAction(howto_connect_neurons)
    howto_menu.addAction(howto_adjust_connection_parameters)

  def __setup_main_frame(self):
    # Create the OpenGL-based VTK widget
    self.__viewer3d = VtkWidget(self, self.__progress_bar)
    self.setCentralWidget(self.__viewer3d)
    self.__controller.set_viewer3d(self.__viewer3d)

    # Add the dock which shows the list of the loaded data (on the left in the main window)
    self.__data_panel = DataPanel(self.__data_container)
    self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.__data_panel)

    # Add the dock which shows the properties of the selected object (on the right in the main window)
    self.__props_panel = PropsPanel(self.__data_container, self.__controller)
    self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.__props_panel)

  def __on_open_project(self):
    project_file_name = QtWidgets.QFileDialog.getOpenFileName(self, "Open a BrainVisPy project", self.__project_folder, r"XML Files (*.xml)")
    if project_file_name[0]:
      # Get the project folder and the project name
      self.__project_folder, project_name = os.path.split(project_file_name[0])
      # Open the project
      error_messages = self.__project_io.open_project(project_file_name[0], self.__data_container, self.__brain, self.__viewer3d)
      # Update the window title
      self.setWindowTitle(project_name + "  -  BrainVisPy")
      if error_messages:
        self.__show_messages(error_messages, "Errors while loading project:")

  def __show_messages(self, messages, title):
    # This guy shows the messages
    message_box = QtWidgets.QDialog(self, QtCore.Qt.Window)
    message_box.setWindowTitle("Message box")
    # Put the messages in a QListWidget
    msg_list = QtWidgets.QListWidget(message_box)
    msg_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
    msg_list.setFocusPolicy(QtCore.Qt.NoFocus)
    for msg in messages:
      msg_list.insertItem(0, msg)
    msg_list.setMinimumWidth(min(self.size().width() / 4, msg_list.sizeHintForColumn(0)))
    # Create the OK button
    ok_btn = QtWidgets.QPushButton("OK")
    ok_btn.setFixedSize(75, 25)
    ok_btn.clicked.connect(message_box.close)
    # Put everything in a layout
    layout = QtWidgets.QVBoxLayout()
    layout.addWidget(QtWidgets.QLabel(title))
    layout.addWidget(msg_list)
    layout.addWidget(ok_btn)
    layout.setAlignment(ok_btn, QtCore.Qt.AlignCenter)
    message_box.setLayout(layout)
    message_box.setModal(True)
    message_box.show()

  def __on_load_files(self):
    # Let the user select the files (file_names[0] will be the list with the file names)
    file_names = QtWidgets.QFileDialog.getOpenFileNames(self, "Load file(s)", self.__load_files_folder, r"All files (*.*)")
    # Load the files. The data_container will notify its observers that new data was loaded.
    if file_names[0]:
      # Take the first file name and extract the folder from it
      self.__load_files_folder = os.path.split(file_names[0][0])[0]
      # Load the files
      self.__project_io.load_files(file_names[0], self.__data_container, self.__viewer3d)

  def __on_import_folder(self):
    folder_name = QtWidgets.QFileDialog.getExistingDirectory(self, "Import folder (load all files from a folder)", self.__load_files_folder)
    # Make sure we got an existing directory
    if os.path.isdir(folder_name):
      self.__load_files_folder = folder_name
      full_file_names = list()
      # Get the *full* file names
      for file_name in os.listdir(folder_name):
        full_file_names.append(folder_name + "/" + file_name) # works on Windows too
      # Load the files. The data_container will notify its observers that new data was loaded.
      self.__project_io.load_files(full_file_names, self.__data_container, self.__viewer3d)

  def __on_save_project(self):
    # Ask the user to specify a project name if necessary
    if self.__project_io.has_file_name():
      self.__save_project()
    else:
      self.__on_save_project_as()

  def __on_save_project_as(self):
    # Ask the user to specify a project name if necessary
    project_file_name = QtWidgets.QFileDialog.getSaveFileName(self, "Save project as", self.__project_folder, r"XML Files (*.xml)")
    if project_file_name[0]:
      ext = os.path.splitext(project_file_name[0])[1].lower()
      if ext and ext == ".xml":
        project_file_name = project_file_name[0]
      else:
        project_file_name = project_file_name[0] + ".xml"
      # Now that we have the right file name, save the project
      self.__project_io.set_file_name(project_file_name)
      self.__save_project()

  def __save_project(self):
    try:
      # Get the project folder and the project name
      self.__project_folder, project_name = os.path.split(self.__project_io.file_name)
      # Save the project
      self.__project_io.save_project(self.__data_container, self.__viewer3d)
      # Update the window title
      self.setWindowTitle(project_name + "  -  BrainVisPy")
    except Exception as err:
      raise err #print(err)

  def __load_config_file(self):
    # First set these default names (in case we fail to open the config file)
    self.__project_folder = "./"
    self.__load_files_folder = "./"
    # Now try to open the file and read the true folder names
    try:
      config = ET.parse(self.__config_file_name).getroot()
      for element in config:
        if element.tag == "project_folder":
          self.__project_folder = element.text
        elif element.tag == "load_files_folder":
          self.__load_files_folder = element.text
    except Exception as exception:
      print("Could not load config stuff: " + str(exception))

  def __on_howto_create_neurons(self):
    dialog = gui.howto.HowtoCreateNeurons(self)
    dialog.show()

  def __on_howto_adjust_neuron_parameters(self):
    dialog = gui.howto.HowtoAdjustNeuronParameters(self)
    dialog.show()

  def __on_howto_connect_neurons(self):
    dialog = gui.howto.HowtoConnectNeurons(self)
    dialog.show()

  def __on_howto_adjust_connection_parameters(self):
    dialog = gui.howto.HowtoAdjustConnectionParameters(self)
    dialog.show()

  def __update_config_file(self):
    try:
      # First, create an XML object for the whole project
      xml_config = ET.Element("BrainVisPy_Config_File")
      # Save the current config stuff
      ET.SubElement(xml_config, "project_folder").text = self.__project_folder
      ET.SubElement(xml_config, "load_files_folder").text = self.__load_files_folder
      # Write the whole XML tree to file
      ET.ElementTree(xml_config).write(self.__config_file_name)
    except Exception as exception:
      print("Could not save the current config stuff: " + str(exception))

  def closeEvent(self, event):
    reply = QtWidgets.QMessageBox.question(self, "Question", "Quit?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
    if reply == QtWidgets.QMessageBox.Yes:
      # Save some info (like the folder of the current project and other stuff) to a config file (to have it for next time)
      self.__update_config_file()
      event.accept()
    else:
      event.ignore()
<commit_msg>Added a get started howto<commit_after>import os
import vtk
from PyQt5 import QtCore, QtWidgets
import xml.etree.ElementTree as ET
import gui.howto
from gui.vtkwidget import VtkWidget
from gui.progress import ProgressBarFrame
from gui.datapanel import DataPanel
from gui.propspanel import PropsPanel
from IO.project import ProjectIO

#==================================================================================================
# MainWindow ======================================================================================
#==================================================================================================
class MainWindow(QtWidgets.QMainWindow):
  def __init__(self, qt_app, controller):
    QtWidgets.QMainWindow.__init__(self)

    # This guy is used by several classes to indicate the progress of the heavy work
    self.__progress_bar = ProgressBarFrame(self, qt_app)

    self.__controller = controller
    self.__data_container = controller.data_container
    self.__brain = controller.brain

    # This guy handles the file/project IO
    self.__project_io = ProjectIO(self.__progress_bar)

    self.__add_menus()
    self.__setup_main_frame()

    # Load some info from a config file (like the folder where the user saved her last project)
    self.__config_file_name = "./brainvispy_config.xml"
    self.__load_config_file()

    # Change the title of the main window
    self.setWindowTitle("BrainVisPy")
    # Make sure that the main window appears (maximized) on the primary screen
    desktop_widget = QtWidgets.QDesktopWidget()
    rect = desktop_widget.availableGeometry(desktop_widget.primaryScreen())
    self.move(rect.x(), rect.y())
    self.showMaximized()

  def __add_menus(self):
    # Menu FILE
    # Open project
    open_project_action = QtWidgets.QAction('Open project', self)
    open_project_action.setShortcut('Ctrl+O')
    open_project_action.triggered.connect(self.__on_open_project)
    # Load file(s)
    load_files_action = QtWidgets.QAction('Load file(s)', self)
    load_files_action.setShortcut('Ctrl+L')
    load_files_action.triggered.connect(self.__on_load_files)
    # Load folder
    load_folder_action = QtWidgets.QAction('Import folder', self)
    load_folder_action.setShortcut('Ctrl+I')
    load_folder_action.triggered.connect(self.__on_import_folder)
    # Save project
    save_project_action = QtWidgets.QAction('Save project', self)
    save_project_action.setShortcut('Ctrl+S')
    save_project_action.triggered.connect(self.__on_save_project)
    # Save as project
    save_project_as_action = QtWidgets.QAction('Save project as', self)
    save_project_as_action.setShortcut('Ctrl+Shift+S')
    save_project_as_action.triggered.connect(self.__on_save_project_as)
    # Quit
    quit_action = QtWidgets.QAction('Quit', self)
    quit_action.setShortcut('Ctrl+Q')
    quit_action.triggered.connect(self.close)

    # HOW TO
    # Get started
    howto_get_started = QtWidgets.QAction('Get started', self)
    howto_get_started.triggered.connect(self.__on_howto_get_started)
    # Create neurons
    howto_create_neurons = QtWidgets.QAction('Create neurons', self)
    howto_create_neurons.triggered.connect(self.__on_howto_create_neurons)
    # Adjust neuron parameters
    howto_adjust_neuron_parameters = QtWidgets.QAction('Adjust neuron parameters', self)
    howto_adjust_neuron_parameters.triggered.connect(self.__on_howto_adjust_neuron_parameters)
    # Connect neurons
    howto_connect_neurons = QtWidgets.QAction('Connect neurons', self)
    howto_connect_neurons.triggered.connect(self.__on_howto_connect_neurons)
    # Adjust connection parameters
    howto_adjust_connection_parameters = QtWidgets.QAction('Adjust connection parameters', self)
    howto_adjust_connection_parameters.triggered.connect(self.__on_howto_adjust_connection_parameters)

    # Add everything to the menu bar
    # FILE
    file_menu = self.menuBar().addMenu("FILE")
    file_menu.addAction(open_project_action)
    file_menu.addAction(save_project_action)
    file_menu.addAction(save_project_as_action)
    file_menu.addSeparator()
    file_menu.addAction(load_files_action)
    file_menu.addAction(load_folder_action)
    file_menu.addSeparator()
    file_menu.addAction(quit_action)
    # HOWTO
    howto_menu = self.menuBar().addMenu("HOW TO")
    howto_menu.addAction(howto_get_started)
    howto_menu.addAction(howto_create_neurons)
    howto_menu.addAction(howto_adjust_neuron_parameters)
    howto_menu.addAction(howto_connect_neurons)
    howto_menu.addAction(howto_adjust_connection_parameters)

  def __setup_main_frame(self):
    # Create the OpenGL-based VTK widget
    self.__viewer3d = VtkWidget(self, self.__progress_bar)
    self.setCentralWidget(self.__viewer3d)
    self.__controller.set_viewer3d(self.__viewer3d)

    # Add the dock which shows the list of the loaded data (on the left in the main window)
    self.__data_panel = DataPanel(self.__data_container)
    self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.__data_panel)

    # Add the dock which shows the properties of the selected object (on the right in the main window)
    self.__props_panel = PropsPanel(self.__data_container, self.__controller)
    self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.__props_panel)

  def __on_open_project(self):
    project_file_name = QtWidgets.QFileDialog.getOpenFileName(self, "Open a BrainVisPy project", self.__project_folder, r"XML Files (*.xml)")
    if project_file_name[0]:
      # Get the project folder and the project name
      self.__project_folder, project_name = os.path.split(project_file_name[0])
      # Open the project
      error_messages = self.__project_io.open_project(project_file_name[0], self.__data_container, self.__brain, self.__viewer3d)
      # Update the window title
      self.setWindowTitle(project_name + "  -  BrainVisPy")
      if error_messages:
        self.__show_messages(error_messages, "Errors while loading project:")

  def __show_messages(self, messages, title):
    # This guy shows the messages
    message_box = QtWidgets.QDialog(self, QtCore.Qt.Window)
    message_box.setWindowTitle("Message box")
    # Put the messages in a QListWidget
    msg_list = QtWidgets.QListWidget(message_box)
    msg_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
    msg_list.setFocusPolicy(QtCore.Qt.NoFocus)
    for msg in messages:
      msg_list.insertItem(0, msg)
    msg_list.setMinimumWidth(min(self.size().width() / 4, msg_list.sizeHintForColumn(0)))
    # Create the OK button
    ok_btn = QtWidgets.QPushButton("OK")
    ok_btn.setFixedSize(75, 25)
    ok_btn.clicked.connect(message_box.close)
    # Put everything in a layout
    layout = QtWidgets.QVBoxLayout()
    layout.addWidget(QtWidgets.QLabel(title))
    layout.addWidget(msg_list)
    layout.addWidget(ok_btn)
    layout.setAlignment(ok_btn, QtCore.Qt.AlignCenter)
    message_box.setLayout(layout)
    message_box.setModal(True)
    message_box.show()

  def __on_load_files(self):
    # Let the user select the files (file_names[0] will be the list with the file names)
    file_names = QtWidgets.QFileDialog.getOpenFileNames(self, "Load file(s)", self.__load_files_folder, r"All files (*.*)")
    # Load the files. The data_container will notify its observers that new data was loaded.
    if file_names[0]:
      # Take the first file name and extract the folder from it
      self.__load_files_folder = os.path.split(file_names[0][0])[0]
      # Load the files
      self.__project_io.load_files(file_names[0], self.__data_container, self.__viewer3d)

  def __on_import_folder(self):
    folder_name = QtWidgets.QFileDialog.getExistingDirectory(self, "Import folder (load all files from a folder)", self.__load_files_folder)
    # Make sure we got an existing directory
    if os.path.isdir(folder_name):
      self.__load_files_folder = folder_name
      full_file_names = list()
      # Get the *full* file names
      for file_name in os.listdir(folder_name):
        full_file_names.append(folder_name + "/" + file_name) # works on Windows too
      # Load the files. The data_container will notify its observers that new data was loaded.
      self.__project_io.load_files(full_file_names, self.__data_container, self.__viewer3d)

  def __on_save_project(self):
    # Ask the user to specify a project name if necessary
    if self.__project_io.has_file_name():
      self.__save_project()
    else:
      self.__on_save_project_as()

  def __on_save_project_as(self):
    # Ask the user to specify a project name if necessary
    project_file_name = QtWidgets.QFileDialog.getSaveFileName(self, "Save project as", self.__project_folder, r"XML Files (*.xml)")
    if project_file_name[0]:
      ext = os.path.splitext(project_file_name[0])[1].lower()
      if ext and ext == ".xml":
        project_file_name = project_file_name[0]
      else:
        project_file_name = project_file_name[0] + ".xml"
      # Now that we have the right file name, save the project
      self.__project_io.set_file_name(project_file_name)
      self.__save_project()

  def __save_project(self):
    try:
      # Get the project folder and the project name
      self.__project_folder, project_name = os.path.split(self.__project_io.file_name)
      # Save the project
      self.__project_io.save_project(self.__data_container, self.__viewer3d)
      # Update the window title
      self.setWindowTitle(project_name + "  -  BrainVisPy")
    except Exception as err:
      raise err #print(err)

  def __load_config_file(self):
    # First set these default names (in case we fail to open the config file)
    self.__project_folder = "./"
    self.__load_files_folder = "./"
    # Now try to open the file and read the true folder names
    try:
      config = ET.parse(self.__config_file_name).getroot()
      for element in config:
        if element.tag == "project_folder":
          self.__project_folder = element.text
        elif element.tag == "load_files_folder":
          self.__load_files_folder = element.text
    except Exception as exception:
      print("Could not load config stuff: " + str(exception))

  def __on_howto_get_started(self):
    dialog = gui.howto.HowtoGetStarted(self)
    dialog.show()

  def __on_howto_create_neurons(self):
    dialog = gui.howto.HowtoCreateNeurons(self)
    dialog.show()

  def __on_howto_adjust_neuron_parameters(self):
    dialog = gui.howto.HowtoAdjustNeuronParameters(self)
    dialog.show()

  def __on_howto_connect_neurons(self):
    dialog = gui.howto.HowtoConnectNeurons(self)
    dialog.show()

  def __on_howto_adjust_connection_parameters(self):
    dialog = gui.howto.HowtoAdjustConnectionParameters(self)
    dialog.show()

  def __update_config_file(self):
    try:
      # First, create an XML object for the whole project
      xml_config = ET.Element("BrainVisPy_Config_File")
      # Save the current config stuff
      ET.SubElement(xml_config, "project_folder").text = self.__project_folder
      ET.SubElement(xml_config, "load_files_folder").text = self.__load_files_folder
      # Write the whole XML tree to file
      ET.ElementTree(xml_config).write(self.__config_file_name)
    except Exception as exception:
      print("Could not save the current config stuff: " + str(exception))

  def closeEvent(self, event):
    reply = QtWidgets.QMessageBox.question(self, "Question", "Quit?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
    if reply == QtWidgets.QMessageBox.Yes:
      # Save some info (like the folder of the current project and other stuff) to a config file (to have it for next time)
      self.__update_config_file()
      event.accept()
    else:
      event.ignore()
<|endoftext|>


<commit_before>"""Integration tests base objects definitions."""

from __future__ import unicode_literals

import os
import unittest
from contextlib import contextmanager

from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.http import QueryDict
from selenium.common import exceptions
from selenium.webdriver.firefox.webdriver import WebDriver
# from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support import ui

try:
    from django.urls import reverse
except ImportError:
    from django.core.urlresolvers import reverse

# Disable selenium as default. Difficult to setup for local tests. Must be enabled
# in CI environment.
USE_SELENIUM = os.getenv('USE_SELENIUM', 0) in (1, True, '1')

def setup_package():
    """Set up the Selenium driver once for all tests."""
    # Just skipping *setup_package* and *teardown_package* generates an
    # uncaught exception under Python 2.6.
    if USE_SELENIUM:
        # Create a Selenium browser instance.
        options = Options()
        options.add_argument('-headless')
        selenium = SeleniumTestCase.selenium = WebDriver(options=options)
        selenium.maximize_window()
        SeleniumTestCase.wait = ui.WebDriverWait(selenium, 5)
        SeleniumTestCase.selenium.implicitly_wait(3)

def teardown_package():
    """Quit the Selenium driver."""
    if USE_SELENIUM:
        SeleniumTestCase.selenium.quit()

@unittest.skipIf(
    not USE_SELENIUM,
    'excluding integration tests: environment variable USE_SELENIUM is not set.')
class SeleniumTestCase(StaticLiveServerTestCase):
    """Base test class for integration tests."""

    PREVIOUS = '<'
    NEXT = '>'
    MORE = 'More results'
    selector = 'div.{0} > h4'

    def setUp(self):
        self.url = self.live_server_url + reverse(self.view_name)

        # Give the browser a little time; Firefox sometimes throws
        # random errors if you hit it too soon
        # time.sleep(1)

#     @classmethod
#     def setUpClass(cls):
#         if not SHOW_BROWSER:
#             # start display
#             cls.xvfb = Xvfb(width=1280, height=720)
#             cls.xvfb.start()

#         # Create a Selenium browser instance.
#         cls.browser = os.getenv('SELENIUM_BROWSER', 'firefox')
#         # start browser
#         if cls.browser == 'firefox':
#             cls.selenium = webdriver.Firefox()
#         elif cls.browser == 'htmlunit':
#             cls.selenium = webdriver.Remote(desired_capabilities=DesiredCapabilities.HTMLUNITWITHJS)
#         elif cls.browser == 'iphone':
#             command_executor = "http://127.0.0.1:3001/wd/hub"
#             cls.selenium = webdriver.Remote(command_executor=command_executor,
#                 desired_capabilities=DesiredCapabilities.IPHONE)
#         elif cls.browser == 'safari':
#             cls.selenium = webdriver.Remote(desired_capabilities={
#                 "browserName": "safari", "version": "",
#                 "platform": "MAC", "javascriptEnabled": True})
#         else:
#             cls.selenium = webdriver.Chrome()
#         cls.selenium.maximize_window()
#         cls.wait = ui.WebDriverWait(cls.selenium, 10)
#         cls.selenium.implicitly_wait(3)
#
#         super(SeleniumTestCase, cls).setUpClass()

#     @classmethod
#     def tearDownClass(cls):
#         # stop browser
#         cls.selenium.quit()
#         super(SeleniumTestCase, cls).tearDownClass()
#         if not SHOW_BROWSER:
#             # stop display
#             cls.xvfb.stop()

    def get(self, url=None, data=None, **kwargs):
        """Load a web page in the current browser session.

        If *url* is None, *self.url* is used.
        The querydict can be expressed providing *data* or *kwargs*.
        """
        if url is None:
            url = self.url

        querydict = QueryDict('', mutable=True)
        if data is not None:
            querydict.update(data)
        querydict.update(kwargs)
        path = '{0}?{1}'.format(url, querydict.urlencode())

        # the following javascript scrolls down the entire page body.  Since Twitter
        # uses "inifinite scrolling", more content will be added to the bottom of the
        # DOM as you scroll... since it is in the loop, it will scroll down up to 100
        # times.
        # for _ in range(100):
        #     self.selenium.execute_script("window.scrollTo(0, document.body.scrollHeight);")

        # print all of the page source that was loaded
        # print self.selenium.page_source.encode("utf-8")
        return self.selenium.get(path)

    def wait_ajax(self):
        """Wait for the document to be ready."""
        def document_ready(driver):
            script = """
                return (
                    document.readyState === 'complete' &&
                    jQuery.active === 0 &&
                    typeof jQuery != 'undefined'
                );
            """
            return driver.execute_script(script)

        self.wait.until(document_ready)
        return self.wait

    def click_link(self, text, index=0):
        """Click the link with the given *text* and *index*."""
        link = self.selenium.find_elements_by_link_text(str(text))[index]
        link.click()
        return link

    def scroll_down(self):
        """Scroll down to the bottom of the page."""
        script = 'window.scrollTo(0, document.body.scrollHeight);'
        self.selenium.execute_script(script)

    def get_current_elements(self, class_name, driver=None):
        """Return the range of current elements as a list of numbers."""
        elements = []
        selector = self.selector.format(class_name)
        if driver is None:
            driver = self.selenium
        for element in driver.find_elements_by_css_selector(selector):
            elements.append(int(element.text.split()[1]))
        return elements

    def asserLinksEqual(self, count, text):
        """Assert the page contains *count* links with given *text*."""
        def link_condition_attended(driver):
            links = driver.find_elements_by_link_text(str(text))
            return len(links) == count
        self.wait.until(link_condition_attended)
        return self.wait

    def assertElements(self, class_name, elements):
        """Assert the current page contains the given *elements*."""
        current_elements = self.get_current_elements(class_name)
        self.assertSequenceEqual(
            elements, current_elements, (
                'Elements differ: {expected} != {actual}\n'
                'Class name: {class_name}\n'
                'Expected elements: {expected}\n'
                'Actual elements: {actual}'
            ).format(
                actual=current_elements,
                expected=elements,
                class_name=class_name,
            )
        )

    @contextmanager
    def assertNewElements(self, class_name, new_elements):
        """Fail when new elements are not found in the page."""
        def new_elements_loaded(driver):
            elements = self.get_current_elements(class_name, driver=driver)
            return elements == new_elements
        yield
        try:
            self.wait_ajax().until(new_elements_loaded)
        except exceptions.TimeoutException:
            self.assertElements(class_name, new_elements)

    @contextmanager
    def assertSameURL(self):
        """Assert the URL does not change after executing the yield block."""
        current_url = self.selenium.current_url
        yield
        self.wait_ajax()
        self.assertEqual(current_url, self.selenium.current_url)
<commit_msg>fix lint<commit_after>"""Integration tests base objects definitions."""

from __future__ import unicode_literals

import os
import unittest
from contextlib import contextmanager

from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.http import QueryDict
from selenium.common import exceptions
# from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.webdriver import WebDriver
from selenium.webdriver.support import ui

try:
    from django.urls import reverse
except ImportError:
    from django.core.urlresolvers import reverse

# Disable selenium as default. Difficult to setup for local tests. Must be enabled
# in CI environment.
USE_SELENIUM = os.getenv('USE_SELENIUM', 0) in (1, True, '1')

def setup_package():
    """Set up the Selenium driver once for all tests."""
    # Just skipping *setup_package* and *teardown_package* generates an
    # uncaught exception under Python 2.6.
    if USE_SELENIUM:
        # Create a Selenium browser instance.
        options = Options()
        options.add_argument('-headless')
        selenium = SeleniumTestCase.selenium = WebDriver(options=options)
        selenium.maximize_window()
        SeleniumTestCase.wait = ui.WebDriverWait(selenium, 5)
        SeleniumTestCase.selenium.implicitly_wait(3)

def teardown_package():
    """Quit the Selenium driver."""
    if USE_SELENIUM:
        SeleniumTestCase.selenium.quit()

@unittest.skipIf(
    not USE_SELENIUM,
    'excluding integration tests: environment variable USE_SELENIUM is not set.')
class SeleniumTestCase(StaticLiveServerTestCase):
    """Base test class for integration tests."""

    PREVIOUS = '<'
    NEXT = '>'
    MORE = 'More results'
    selector = 'div.{0} > h4'

    def setUp(self):
        self.url = self.live_server_url + reverse(self.view_name)

        # Give the browser a little time; Firefox sometimes throws
        # random errors if you hit it too soon
        # time.sleep(1)

#     @classmethod
#     def setUpClass(cls):
#         if not SHOW_BROWSER:
#             # start display
#             cls.xvfb = Xvfb(width=1280, height=720)
#             cls.xvfb.start()

#         # Create a Selenium browser instance.
#         cls.browser = os.getenv('SELENIUM_BROWSER', 'firefox')
#         # start browser
#         if cls.browser == 'firefox':
#             cls.selenium = webdriver.Firefox()
#         elif cls.browser == 'htmlunit':
#             cls.selenium = webdriver.Remote(desired_capabilities=DesiredCapabilities.HTMLUNITWITHJS)
#         elif cls.browser == 'iphone':
#             command_executor = "http://127.0.0.1:3001/wd/hub"
#             cls.selenium = webdriver.Remote(command_executor=command_executor,
#                 desired_capabilities=DesiredCapabilities.IPHONE)
#         elif cls.browser == 'safari':
#             cls.selenium = webdriver.Remote(desired_capabilities={
#                 "browserName": "safari", "version": "",
#                 "platform": "MAC", "javascriptEnabled": True})
#         else:
#             cls.selenium = webdriver.Chrome()
#         cls.selenium.maximize_window()
#         cls.wait = ui.WebDriverWait(cls.selenium, 10)
#         cls.selenium.implicitly_wait(3)
#
#         super(SeleniumTestCase, cls).setUpClass()

#     @classmethod
#     def tearDownClass(cls):
#         # stop browser
#         cls.selenium.quit()
#         super(SeleniumTestCase, cls).tearDownClass()
#         if not SHOW_BROWSER:
#             # stop display
#             cls.xvfb.stop()

    def get(self, url=None, data=None, **kwargs):
        """Load a web page in the current browser session.

        If *url* is None, *self.url* is used.
        The querydict can be expressed providing *data* or *kwargs*.
        """
        if url is None:
            url = self.url

        querydict = QueryDict('', mutable=True)
        if data is not None:
            querydict.update(data)
        querydict.update(kwargs)
        path = '{0}?{1}'.format(url, querydict.urlencode())

        # the following javascript scrolls down the entire page body.  Since Twitter
        # uses "inifinite scrolling", more content will be added to the bottom of the
        # DOM as you scroll... since it is in the loop, it will scroll down up to 100
        # times.
        # for _ in range(100):
        #     self.selenium.execute_script("window.scrollTo(0, document.body.scrollHeight);")

        # print all of the page source that was loaded
        # print self.selenium.page_source.encode("utf-8")
        return self.selenium.get(path)

    def wait_ajax(self):
        """Wait for the document to be ready."""
        def document_ready(driver):
            script = """
                return (
                    document.readyState === 'complete' &&
                    jQuery.active === 0 &&
                    typeof jQuery != 'undefined'
                );
            """
            return driver.execute_script(script)

        self.wait.until(document_ready)
        return self.wait

    def click_link(self, text, index=0):
        """Click the link with the given *text* and *index*."""
        link = self.selenium.find_elements_by_link_text(str(text))[index]
        link.click()
        return link

    def scroll_down(self):
        """Scroll down to the bottom of the page."""
        script = 'window.scrollTo(0, document.body.scrollHeight);'
        self.selenium.execute_script(script)

    def get_current_elements(self, class_name, driver=None):
        """Return the range of current elements as a list of numbers."""
        elements = []
        selector = self.selector.format(class_name)
        if driver is None:
            driver = self.selenium
        for element in driver.find_elements_by_css_selector(selector):
            elements.append(int(element.text.split()[1]))
        return elements

    def asserLinksEqual(self, count, text):
        """Assert the page contains *count* links with given *text*."""
        def link_condition_attended(driver):
            links = driver.find_elements_by_link_text(str(text))
            return len(links) == count
        self.wait.until(link_condition_attended)
        return self.wait

    def assertElements(self, class_name, elements):
        """Assert the current page contains the given *elements*."""
        current_elements = self.get_current_elements(class_name)
        self.assertSequenceEqual(
            elements, current_elements, (
                'Elements differ: {expected} != {actual}\n'
                'Class name: {class_name}\n'
                'Expected elements: {expected}\n'
                'Actual elements: {actual}'
            ).format(
                actual=current_elements,
                expected=elements,
                class_name=class_name,
            )
        )

    @contextmanager
    def assertNewElements(self, class_name, new_elements):
        """Fail when new elements are not found in the page."""
        def new_elements_loaded(driver):
            elements = self.get_current_elements(class_name, driver=driver)
            return elements == new_elements
        yield
        try:
            self.wait_ajax().until(new_elements_loaded)
        except exceptions.TimeoutException:
            self.assertElements(class_name, new_elements)

    @contextmanager
    def assertSameURL(self):
        """Assert the URL does not change after executing the yield block."""
        current_url = self.selenium.current_url
        yield
        self.wait_ajax()
        self.assertEqual(current_url, self.selenium.current_url)
<|endoftext|>


<commit_before>import logging
import re

from django.conf import settings
from django.utils.termcolors import colorize
try:
    # Django 1.x
    from django.core.urlresolvers import resolve
except ImportError:
    # Django 2.x
    from django.urls import resolve

DEFAULT_LOG_LEVEL = logging.DEBUG
DEFAULT_COLORIZE = True
DEFAULT_MAX_BODY_LENGTH = 50000  # log no more than 3k bytes of content
SETTING_NAMES = {
    'log_level': 'REQUEST_LOGGING_DATA_LOG_LEVEL',
    'legacy_colorize': 'REQUEST_LOGGING_DISABLE_COLORIZE',
    'colorize': 'REQUEST_LOGGING_ENABLE_COLORIZE',
    'max_body_length': 'REQUEST_LOGGING_MAX_BODY_LENGTH'
}
BINARY_REGEX = re.compile(r'(.+Content-Type:.*?)(\S+)/(\S+)(?:\r\n)*(.+)', re.S | re.I)
BINARY_TYPES = ('image', 'application')
NO_LOGGING_ATTR = 'no_logging'
NO_LOGGING_MSG = 'No logging for this endpoint'
request_logger = logging.getLogger('django.request')

class Logger:
    def log(self, level, msg, logging_context):
        args = logging_context['args']
        kwargs = logging_context['kwargs']
        for line in re.split(r'\r?\n', str(msg)):
            request_logger.log(level, line, *args, **kwargs)

    def log_error(self, level, msg, logging_context):
        self.log(level, msg, logging_context)

class ColourLogger(Logger):
    def __init__(self, log_colour, log_error_colour):
        self.log_colour = log_colour
        self.log_error_colour = log_error_colour

    def log(self, level, msg, logging_context):
        colour = self.log_error_colour if level >= logging.ERROR else self.log_colour
        self._log(level, msg, colour, logging_context)

    def log_error(self, level, msg, logging_context):
        # Forces colour to be log_error_colour no matter what level is
        self._log(level, msg, self.log_error_colour, logging_context)

    def _log(self, level, msg, colour, logging_context):
        args = logging_context['args']
        kwargs = logging_context['kwargs']
        for line in re.split(r'\r?\n', str(msg)):
            line = colorize(line, fg=colour)
            request_logger.log(level, line, *args, **kwargs)

class LoggingMiddleware(object):
    def __init__(self, get_response=None):
        self.get_response = get_response

        self.log_level = getattr(settings, SETTING_NAMES['log_level'], DEFAULT_LOG_LEVEL)
        if self.log_level not in [logging.NOTSET, logging.DEBUG, logging.INFO,
                                  logging.WARNING, logging.ERROR, logging.CRITICAL]:
            raise ValueError("Unknown log level({}) in setting({})".format(self.log_level, SETTING_NAMES['log_level']))

        # TODO: remove deprecated legacy settings
        enable_colorize = getattr(settings, SETTING_NAMES['legacy_colorize'], None)
        if enable_colorize is None:
            enable_colorize = getattr(settings, SETTING_NAMES['colorize'], DEFAULT_COLORIZE)

        if not isinstance(enable_colorize, bool):
            raise ValueError(
                "{} should be boolean. {} is not boolean.".format(SETTING_NAMES['colorize'], enable_colorize)
            )

        self.max_body_length = getattr(settings, SETTING_NAMES['max_body_length'], DEFAULT_MAX_BODY_LENGTH)
        if not isinstance(self.max_body_length, int):
            raise ValueError(
                "{} should be int. {} is not int.".format(SETTING_NAMES['max_body_length'], self.max_body_length)
            )

        self.logger = ColourLogger("cyan", "magenta") if enable_colorize else Logger()
        self.boundary = ''

    def __call__(self, request):
        self.process_request( request )
        response = self.get_response( request )
        self.process_response( request, response )
        return response

    def process_request(self, request):
        skip_logging_because = self._should_log_route(request)
        if skip_logging_because:
            return self._skip_logging_request(request, skip_logging_because)
        else:
            return self._log_request(request)

    def _should_log_route(self, request):
        try:
            route_match = resolve(request.path)
        except:
            return None

        method = request.method.lower()
        view = route_match.func
        func = view
        # This is for "django rest framework"
        if hasattr(view, 'cls'):
            if hasattr(view, 'actions'):
                actions = view.actions
                if actions[method]:
                    func = getattr(view.cls, view.actions[method], None)
            else:
                func = getattr(view.cls, method, None)
        elif hasattr(view, 'view_class'):
            # This is for django class-based views
            func = getattr(view.view_class, method, None)
        no_logging = getattr(func, NO_LOGGING_ATTR, None)
        return no_logging

    def _skip_logging_request(self, request, reason):
        method_path = "{} {}".format(request.method, request.get_full_path())
        no_log_context = {
            'args': (),
            'kwargs': {
                'extra': {
                    'no_logging': reason
                },
            },
        }
        self.logger.log(logging.INFO, method_path + " (not logged because '" + reason + "')", no_log_context)

    def _log_request(self, request):
        method_path = "{} {}".format(request.method, request.get_full_path())

        logging_context = self._get_logging_context(request, None)
        self.logger.log(logging.INFO, method_path, logging_context)
        self._log_request_headers(request, logging_context)
        self._log_request_body(request, logging_context)

    def _log_request_headers(self, request, logging_context):
        headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}

        if headers:
            self.logger.log(self.log_level, headers, logging_context)

    def _log_request_body(self, request, logging_context):
        if request.body:
            content_type = request.META.get('CONTENT_TYPE', '')
            is_multipart = content_type.startswith('multipart/form-data')
            if is_multipart:
                self.boundary = '--' + content_type[30:]  # First 30 characters are "multipart/form-data; boundary="
            if is_multipart:
                self._log_multipart(self._chunked_to_max(request.body), logging_context)
            else:
                self.logger.log(self.log_level, self._chunked_to_max(request.body), logging_context)

    def process_response(self, request, response):
        resp_log = "{} {} - {}".format(request.method, request.get_full_path(), response.status_code)
        skip_logging_because = self._should_log_route(request)
        if skip_logging_because:
            self.logger.log_error(logging.INFO, resp_log, {'args': {}, 'kwargs': { 'no_logging': skip_logging_because }})
            return response
        logging_context = self._get_logging_context(request, response)

        if response.status_code in range(400, 600):
            self.logger.log_error(logging.INFO, resp_log, logging_context)
            self._log_resp(logging.ERROR, response, logging_context)
        else:
            self.logger.log(logging.INFO, resp_log, logging_context)
            self._log_resp(self.log_level, response, logging_context)

        return response

    def _get_logging_context(self, request, response):
        """
        Returns a map with args and kwargs to provide additional context to calls to logging.log().
        This allows the logging context to be created per process request/response call.
        """
        return {
            'args': (),
            'kwargs': {
                'extra': {
                    'request': request,
                    'response': response,
                },
            },
        }

    def _log_multipart(self, body, logging_context):
        """
        Splits multipart body into parts separated by "boundary", then matches each part to BINARY_REGEX
        which searches for existance of "Content-Type" and capture of what type is this part.
        If it is an image or an application replace that content with "(binary data)" string.
        This function will log "(multipart/form)" if body can't be decoded by utf-8.
        """
        try:
            body_str = body if isinstance(body, str) else body.decode()
        except UnicodeDecodeError:
            self.logger.log(self.log_level, "(multipart/form)", logging_context)
            return

        parts = body_str.split(self.boundary)
        last = len(parts) - 1
        for i, part in enumerate(parts):
            if 'Content-Type:' in part:
                match = BINARY_REGEX.search(part)
                if match and match.group(2) in BINARY_TYPES and not match.group(4) in ('', '\r\n'):
                    part = match.expand(r'\1\2/\3\r\n\r\n(binary data)\r\n')

            if i != last:
                part = part + self.boundary

            self.logger.log(self.log_level, part, logging_context)

    def _log_resp(self, level, response, logging_context):
        if re.match('^application/json', response.get('Content-Type', ''), re.I):
            self.logger.log(level, response._headers, logging_context)
            self.logger.log(level, self._chunked_to_max(response.content), logging_context)

    def _chunked_to_max(self, msg):
        return msg[0:self.max_body_length]
<commit_msg>[attrs] Another way to map DRF views without rasising an exception<commit_after>import logging
import re

from django.conf import settings
from django.utils.termcolors import colorize
try:
    # Django 1.x
    from django.core.urlresolvers import resolve
except ImportError:
    # Django 2.x
    from django.urls import resolve

DEFAULT_LOG_LEVEL = logging.DEBUG
DEFAULT_COLORIZE = True
DEFAULT_MAX_BODY_LENGTH = 50000  # log no more than 3k bytes of content
SETTING_NAMES = {
    'log_level': 'REQUEST_LOGGING_DATA_LOG_LEVEL',
    'legacy_colorize': 'REQUEST_LOGGING_DISABLE_COLORIZE',
    'colorize': 'REQUEST_LOGGING_ENABLE_COLORIZE',
    'max_body_length': 'REQUEST_LOGGING_MAX_BODY_LENGTH'
}
BINARY_REGEX = re.compile(r'(.+Content-Type:.*?)(\S+)/(\S+)(?:\r\n)*(.+)', re.S | re.I)
BINARY_TYPES = ('image', 'application')
NO_LOGGING_ATTR = 'no_logging'
NO_LOGGING_MSG = 'No logging for this endpoint'
request_logger = logging.getLogger('django.request')

class Logger:
    def log(self, level, msg, logging_context):
        args = logging_context['args']
        kwargs = logging_context['kwargs']
        for line in re.split(r'\r?\n', str(msg)):
            request_logger.log(level, line, *args, **kwargs)

    def log_error(self, level, msg, logging_context):
        self.log(level, msg, logging_context)

class ColourLogger(Logger):
    def __init__(self, log_colour, log_error_colour):
        self.log_colour = log_colour
        self.log_error_colour = log_error_colour

    def log(self, level, msg, logging_context):
        colour = self.log_error_colour if level >= logging.ERROR else self.log_colour
        self._log(level, msg, colour, logging_context)

    def log_error(self, level, msg, logging_context):
        # Forces colour to be log_error_colour no matter what level is
        self._log(level, msg, self.log_error_colour, logging_context)

    def _log(self, level, msg, colour, logging_context):
        args = logging_context['args']
        kwargs = logging_context['kwargs']
        for line in re.split(r'\r?\n', str(msg)):
            line = colorize(line, fg=colour)
            request_logger.log(level, line, *args, **kwargs)

class LoggingMiddleware(object):
    def __init__(self, get_response=None):
        self.get_response = get_response

        self.log_level = getattr(settings, SETTING_NAMES['log_level'], DEFAULT_LOG_LEVEL)
        if self.log_level not in [logging.NOTSET, logging.DEBUG, logging.INFO,
                                  logging.WARNING, logging.ERROR, logging.CRITICAL]:
            raise ValueError("Unknown log level({}) in setting({})".format(self.log_level, SETTING_NAMES['log_level']))

        # TODO: remove deprecated legacy settings
        enable_colorize = getattr(settings, SETTING_NAMES['legacy_colorize'], None)
        if enable_colorize is None:
            enable_colorize = getattr(settings, SETTING_NAMES['colorize'], DEFAULT_COLORIZE)

        if not isinstance(enable_colorize, bool):
            raise ValueError(
                "{} should be boolean. {} is not boolean.".format(SETTING_NAMES['colorize'], enable_colorize)
            )

        self.max_body_length = getattr(settings, SETTING_NAMES['max_body_length'], DEFAULT_MAX_BODY_LENGTH)
        if not isinstance(self.max_body_length, int):
            raise ValueError(
                "{} should be int. {} is not int.".format(SETTING_NAMES['max_body_length'], self.max_body_length)
            )

        self.logger = ColourLogger("cyan", "magenta") if enable_colorize else Logger()
        self.boundary = ''

    def __call__(self, request):
        self.process_request( request )
        response = self.get_response( request )
        self.process_response( request, response )
        return response

    def process_request(self, request):
        skip_logging_because = self._should_log_route(request)
        if skip_logging_because:
            return self._skip_logging_request(request, skip_logging_because)
        else:
            return self._log_request(request)

    def _should_log_route(self, request):
        try:
            route_match = resolve(request.path)
        except:
            return None

        method = request.method.lower()
        view = route_match.func
        func = view
        # This is for "django rest framework"
        if hasattr(view, 'cls'):
            if hasattr(view, 'actions'):
                actions = view.actions
                method_name = actions.get(method)
                if method_name:
                    func = getattr(view.cls, view.actions[method], None)
            else:
                func = getattr(view.cls, method, None)
        elif hasattr(view, 'view_class'):
            # This is for django class-based views
            func = getattr(view.view_class, method, None)
        no_logging = getattr(func, NO_LOGGING_ATTR, None)
        return no_logging

    def _skip_logging_request(self, request, reason):
        method_path = "{} {}".format(request.method, request.get_full_path())
        no_log_context = {
            'args': (),
            'kwargs': {
                'extra': {
                    'no_logging': reason
                },
            },
        }
        self.logger.log(logging.INFO, method_path + " (not logged because '" + reason + "')", no_log_context)

    def _log_request(self, request):
        method_path = "{} {}".format(request.method, request.get_full_path())

        logging_context = self._get_logging_context(request, None)
        self.logger.log(logging.INFO, method_path, logging_context)
        self._log_request_headers(request, logging_context)
        self._log_request_body(request, logging_context)

    def _log_request_headers(self, request, logging_context):
        headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}

        if headers:
            self.logger.log(self.log_level, headers, logging_context)

    def _log_request_body(self, request, logging_context):
        if request.body:
            content_type = request.META.get('CONTENT_TYPE', '')
            is_multipart = content_type.startswith('multipart/form-data')
            if is_multipart:
                self.boundary = '--' + content_type[30:]  # First 30 characters are "multipart/form-data; boundary="
            if is_multipart:
                self._log_multipart(self._chunked_to_max(request.body), logging_context)
            else:
                self.logger.log(self.log_level, self._chunked_to_max(request.body), logging_context)

    def process_response(self, request, response):
        resp_log = "{} {} - {}".format(request.method, request.get_full_path(), response.status_code)
        skip_logging_because = self._should_log_route(request)
        if skip_logging_because:
            self.logger.log_error(logging.INFO, resp_log, {'args': {}, 'kwargs': { 'no_logging': skip_logging_because }})
            return response
        logging_context = self._get_logging_context(request, response)

        if response.status_code in range(400, 600):
            self.logger.log_error(logging.INFO, resp_log, logging_context)
            self._log_resp(logging.ERROR, response, logging_context)
        else:
            self.logger.log(logging.INFO, resp_log, logging_context)
            self._log_resp(self.log_level, response, logging_context)

        return response

    def _get_logging_context(self, request, response):
        """
        Returns a map with args and kwargs to provide additional context to calls to logging.log().
        This allows the logging context to be created per process request/response call.
        """
        return {
            'args': (),
            'kwargs': {
                'extra': {
                    'request': request,
                    'response': response,
                },
            },
        }

    def _log_multipart(self, body, logging_context):
        """
        Splits multipart body into parts separated by "boundary", then matches each part to BINARY_REGEX
        which searches for existance of "Content-Type" and capture of what type is this part.
        If it is an image or an application replace that content with "(binary data)" string.
        This function will log "(multipart/form)" if body can't be decoded by utf-8.
        """
        try:
            body_str = body if isinstance(body, str) else body.decode()
        except UnicodeDecodeError:
            self.logger.log(self.log_level, "(multipart/form)", logging_context)
            return

        parts = body_str.split(self.boundary)
        last = len(parts) - 1
        for i, part in enumerate(parts):
            if 'Content-Type:' in part:
                match = BINARY_REGEX.search(part)
                if match and match.group(2) in BINARY_TYPES and not match.group(4) in ('', '\r\n'):
                    part = match.expand(r'\1\2/\3\r\n\r\n(binary data)\r\n')

            if i != last:
                part = part + self.boundary

            self.logger.log(self.log_level, part, logging_context)

    def _log_resp(self, level, response, logging_context):
        if re.match('^application/json', response.get('Content-Type', ''), re.I):
            self.logger.log(level, response._headers, logging_context)
            self.logger.log(level, self._chunked_to_max(response.content), logging_context)

    def _chunked_to_max(self, msg):
        return msg[0:self.max_body_length]
<|endoftext|>


<commit_before>VARNISH_SERVERS = ['http://127.0.0.1:8080']
ENABLE_VARNISH = True
ENABLE_ESI = False

REST_FRAMEWORK = {
    'PAGE_SIZE': 10,
    # Order is important here because of a bug in rest_framework_swagger. For now,
    # rest_framework.renderers.JSONRenderer needs to be first, at least until
    # https://github.com/marcgibbons/django-rest-swagger/issues/271 is resolved.
    'DEFAULT_RENDERER_CLASSES': (
        'api.base.renderers.JSONAPIRenderer',
        'api.base.renderers.JSONRendererWithESISupport',
        'api.base.renderers.BrowsableAPIRendererNoForms',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'api.base.parsers.JSONAPIParser',
        'api.base.parsers.JSONAPIParserForRegularJSON',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),
    'EXCEPTION_HANDLER': 'api.base.exceptions.json_api_exception_handler',
    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'api.base.content_negotiation.JSONAPIContentNegotiation',
    'DEFAULT_VERSIONING_CLASS': 'api.base.versioning.BaseVersioning',
    'DEFAULT_VERSION': '2.0',
    # The versions below are specifically for testing purposes and do not reflect the actual versioning of the API.
    # If changes are made to this list, or to DEFAULT_VERSION above, please reflect those changes in
    # api_tests/base/test_versioning.py so that local tests will pass.
    'ALLOWED_VERSIONS': (
        '2.0',
        '2.0.1',
        '2.1',
        '2.2',
        '3.0',
        '3.0.1',
    ),
    'DEFAULT_FILTER_BACKENDS': ('api.base.filters.ODMOrderingFilter',),
    'DEFAULT_PAGINATION_CLASS': 'api.base.pagination.JSONAPIPagination',
    'ORDERING_PARAM': 'sort',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # Custom auth classes
        'api.base.authentication.drf.OSFBasicAuthentication',
        'api.base.authentication.drf.OSFSessionAuthentication',
        'api.base.authentication.drf.OSFCASAuthentication'
    ),
    'DEFAULT_THROTTLE_CLASSES': [],
    'DEFAULT_THROTTLE_RATES': {}
}
<commit_msg>Add back default throttling rates for travis<commit_after>VARNISH_SERVERS = ['http://127.0.0.1:8080']
ENABLE_VARNISH = True
ENABLE_ESI = False

REST_FRAMEWORK = {
    'PAGE_SIZE': 10,
    # Order is important here because of a bug in rest_framework_swagger. For now,
    # rest_framework.renderers.JSONRenderer needs to be first, at least until
    # https://github.com/marcgibbons/django-rest-swagger/issues/271 is resolved.
    'DEFAULT_RENDERER_CLASSES': (
        'api.base.renderers.JSONAPIRenderer',
        'api.base.renderers.JSONRendererWithESISupport',
        'api.base.renderers.BrowsableAPIRendererNoForms',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'api.base.parsers.JSONAPIParser',
        'api.base.parsers.JSONAPIParserForRegularJSON',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),
    'EXCEPTION_HANDLER': 'api.base.exceptions.json_api_exception_handler',
    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'api.base.content_negotiation.JSONAPIContentNegotiation',
    'DEFAULT_VERSIONING_CLASS': 'api.base.versioning.BaseVersioning',
    'DEFAULT_VERSION': '2.0',
    # The versions below are specifically for testing purposes and do not reflect the actual versioning of the API.
    # If changes are made to this list, or to DEFAULT_VERSION above, please reflect those changes in
    # api_tests/base/test_versioning.py so that local tests will pass.
    'ALLOWED_VERSIONS': (
        '2.0',
        '2.0.1',
        '2.1',
        '2.2',
        '3.0',
        '3.0.1',
    ),
    'DEFAULT_FILTER_BACKENDS': ('api.base.filters.ODMOrderingFilter',),
    'DEFAULT_PAGINATION_CLASS': 'api.base.pagination.JSONAPIPagination',
    'ORDERING_PARAM': 'sort',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # Custom auth classes
        'api.base.authentication.drf.OSFBasicAuthentication',
        'api.base.authentication.drf.OSFSessionAuthentication',
        'api.base.authentication.drf.OSFCASAuthentication'
    ),
    'DEFAULT_THROTTLE_CLASSES': [],
    'DEFAULT_THROTTLE_RATES': {
        'user': '1000000/hour',
        'non-cookie-auth': '1000000/hour',
        'add-contributor': '10/second',
        'create-guid': '1000/hour',
        'root-anon-throttle': '1000/hour',
        'test-user': '2/hour',
        'test-anon': '1/hour',
    }
}
<|endoftext|>


<commit_before>"""
digimesh.py

By Matteo Lucchesi, 2011
Inspired by code written by Amit Synderman, Marco Sangalli and Paul Malmsten
matteo@luccalug.it http://matteo.luccalug.it

This module provides an XBee (Digimesh) API library.

Updated by Thom Nichols http://blog.thomnichols.org
"""
import struct
from xbee.base import XBeeBase

class DigiMesh(XBeeBase):
    """
    Provides an implementation of the XBee API for Digimesh modules
    with recent firmware.

    Commands may be sent to a device by instansiating this class with
    a serial port object (see PySerial) and then calling the send
    method with the proper information specified by the API. Data may
    be read from a device syncronously by calling wait_read_frame. For
    asynchronous reads, see the definition of XBeeBase.
    """
    # Packets which can be sent to an XBee

    # Format:
    #        {name of command:
    #           [{name:field name, len:field length, default: default value sent}
    #            ...
    #            ]
    #         ...
    #         }
    api_commands = {"at":
                        [{'name':'id',        'len':1,      'default':'\x08'},
                         {'name':'frame_id',  'len':1,      'default':'\x00'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "queued_at":
                        [{'name':'id',        'len':1,      'default':'\x09'},
                         {'name':'frame_id',  'len':1,      'default':'\x00'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    #explicit adrresing command frame - to do!
                    "remote_at":
                        [{'name':'id',              'len':1,        'default':'\x17'},
                         {'name':'frame_id',        'len':1,        'default':'\x00'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'reserved',        'len':2,        'default':'\xFF\xFE'},
                         {'name':'options',         'len':1,        'default':'\x02'},
                         {'name':'command',         'len':2,        'default':None},
                         {'name':'parameter',       'len':None,     'default':None}],
                    "tx":
                        [{'name':'id',              'len':1,        'default':'\x10'},
                         {'name':'frame_id',        'len':1,        'default':'\x00'},
                         {'name':'dest_addr',       'len':8,        'default':None},
                         {'name':'reserved',        'len':2,         'default':'\xFF\xFE'},
                         {'name':'broadcast_radius', 'len':1,         'default':'\x00'},
                         {'name':'options',         'len':1,        'default':'\x00'},
                         {'name':'data',            'len':None,     'default':None}],

                    }

    # Packets which can be received from an XBee

    # Format:
    #        {id byte received from XBee:
    #           {name: name of response
    #            structure:
    #                [ {'name': name of field, 'len':length of field}
    #                  ...
    #                  ]
    #            parse_as_io_samples:name of field to parse as io
    #           }
    #           ...
    #        }
    #
    api_responses = {b"\x88":
                        {'name':'at_response',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'command',     'len':2},
                             {'name':'status',      'len':1},
                             {'name':'parameter',   'len':None}]},
                     b"\x8a":
                        {'name':'status',
                         'structure':
                            [{'name':'status',      'len':1}]},
                     b"\x8b":
                        {'name':'tx_status',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'reserved',        'len':2, 'default':'\xFF\xFE'},
                             {'name':'retries',         'len':1},
                             {'name':'deliver_status',  'len':1},
                             {'name':'discover_status', 'len':1}]},
                     b"\x90":
                        {'name':'rx',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'source_addr', 'len':7},
                             {'name':'reserved',    'len':2},
                             {'name':'options',     'len':1},
                             {'name':'data',        'len':None}]},
                    # b"\x91": to do!
                    #    {'name':'explicit_rx_indicator',
                    #     'structure':
                    #        [{'name':'source_addr', 'len':2},
                    #         {'name':'rssi',        'len':1},
                    #         {'name':'options',     'len':1},
                    #         {'name':'rf_data',     'len':None}]},
                    # b"\x92": data sample rx indicator {}
                     b"\x95":
                        {'name':'node_id',
                         'structure':
                            [{'name':'frame_id',          'len':1},
                             {'name':'source_addr_long',  'len':8},
                             {'name':'network_addr',      'len':2},
                             {'name':'options',           'len':1},
                             {'name':'source_addr',       'len':2},
                             {'name':'network_addr_long', 'len':8},
                             {'name':'node_id',           'len':'null_terminated'},
                             {'name':'parent',            'len':1}]},

                     b"\x97":
                        {'name':'remote_at_response',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'source_addr',     'len':8},
                             {'name':'reserved',        'len':2},
                             {'name':'command',         'len':2},
                             {'name':'status',          'len':1},
                             {'name':'parameter',       'len':None}]}
                     }

    def __init__(self, *args, **kwargs):
        # Call the super class constructor to save the serial port
        super(DigiMesh, self).__init__(*args, **kwargs)
<commit_msg>900 digimesh has additional bytes in the NI frame<commit_after>"""
digimesh.py

By Matteo Lucchesi, 2011
Inspired by code written by Amit Synderman, Marco Sangalli and Paul Malmsten
matteo@luccalug.it http://matteo.luccalug.it

This module provides an XBee (Digimesh) API library.

Updated by Thom Nichols http://blog.thomnichols.org
"""
import struct
from xbee.base import XBeeBase

class DigiMesh(XBeeBase):
    """
    Provides an implementation of the XBee API for Digimesh modules
    with recent firmware.

    Commands may be sent to a device by instansiating this class with
    a serial port object (see PySerial) and then calling the send
    method with the proper information specified by the API. Data may
    be read from a device syncronously by calling wait_read_frame. For
    asynchronous reads, see the definition of XBeeBase.
    """
    # Packets which can be sent to an XBee

    # Format:
    #        {name of command:
    #           [{name:field name, len:field length, default: default value sent}
    #            ...
    #            ]
    #         ...
    #         }
    api_commands = {"at":
                        [{'name':'id',        'len':1,      'default':'\x08'},
                         {'name':'frame_id',  'len':1,      'default':'\x00'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "queued_at":
                        [{'name':'id',        'len':1,      'default':'\x09'},
                         {'name':'frame_id',  'len':1,      'default':'\x00'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    #explicit adrresing command frame - to do!
                    "remote_at":
                        [{'name':'id',              'len':1,        'default':'\x17'},
                         {'name':'frame_id',        'len':1,        'default':'\x00'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'reserved',        'len':2,        'default':'\xFF\xFE'},
                         {'name':'options',         'len':1,        'default':'\x02'},
                         {'name':'command',         'len':2,        'default':None},
                         {'name':'parameter',       'len':None,     'default':None}],
                    "tx":
                        [{'name':'id',              'len':1,        'default':'\x10'},
                         {'name':'frame_id',        'len':1,        'default':'\x00'},
                         {'name':'dest_addr',       'len':8,        'default':None},
                         {'name':'reserved',        'len':2,         'default':'\xFF\xFE'},
                         {'name':'broadcast_radius', 'len':1,         'default':'\x00'},
                         {'name':'options',         'len':1,        'default':'\x00'},
                         {'name':'data',            'len':None,     'default':None}],

                    }

    # Packets which can be received from an XBee

    # Format:
    #        {id byte received from XBee:
    #           {name: name of response
    #            structure:
    #                [ {'name': name of field, 'len':length of field}
    #                  ...
    #                  ]
    #            parse_as_io_samples:name of field to parse as io
    #           }
    #           ...
    #        }
    #
    api_responses = {b"\x88":
                        {'name':'at_response',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'command',     'len':2},
                             {'name':'status',      'len':1},
                             {'name':'parameter',   'len':None}]},
                     b"\x8a":
                        {'name':'status',
                         'structure':
                            [{'name':'status',      'len':1}]},
                     b"\x8b":
                        {'name':'tx_status',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'reserved',        'len':2, 'default':'\xFF\xFE'},
                             {'name':'retries',         'len':1},
                             {'name':'deliver_status',  'len':1},
                             {'name':'discover_status', 'len':1}]},
                     b"\x90":
                        {'name':'rx',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'source_addr', 'len':7},
                             {'name':'reserved',    'len':2},
                             {'name':'options',     'len':1},
                             {'name':'data',        'len':None}]},
                    # b"\x91": to do!
                    #    {'name':'explicit_rx_indicator',
                    #     'structure':
                    #        [{'name':'source_addr', 'len':2},
                    #         {'name':'rssi',        'len':1},
                    #         {'name':'options',     'len':1},
                    #         {'name':'rf_data',     'len':None}]},
                    # b"\x92": data sample rx indicator {}
                     b"\x95":
                        {'name':'node_id',
                         'structure':
                            [{'name':'source_addr_long',  'len':8},
                             {'name':'network_addr',      'len':2},
                             {'name':'options',           'len':1},
                             {'name':'source_addr',       'len':2},
                             {'name':'network_addr_long', 'len':8},
                             {'name':'node_id',           'len':'null_terminated'},
                             {'name':'parent',            'len':2},
                             {'name':'unknown',           'len':6}]},

                     b"\x97":
                        {'name':'remote_at_response',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'source_addr',     'len':8},
                             {'name':'reserved',        'len':2},
                             {'name':'command',         'len':2},
                             {'name':'status',          'len':1},
                             {'name':'parameter',       'len':None}]}
                     }

    def __init__(self, *args, **kwargs):
        # Call the super class constructor to save the serial port
        super(DigiMesh, self).__init__(*args, **kwargs)
<|endoftext|>


<commit_before>"""
A utility that, once imported, kicks the qt event loop at regular
intervals. This lets qt and asyncio peacefully coexist, and it is
necessary for matplotlib < 1.5.0.
"""
import asyncio
import matplotlib.backends.backend_qt5
from matplotlib.backends.backend_qt5 import _create_qApp

_create_qApp()
qApp = matplotlib.backends.backend_qt5.qApp

from matplotlib._pylab_helpers import Gcf

try:
    _draw_all = Gcf.draw_all  # mpl version >= 1.5
except AttributeError:
    # slower, but backward-compatible
    def _draw_all():
        for f_mgr in Gcf.get_all_fig_managers():
            f_mgr.canvas.draw_idle()

def _qt_kicker():
    # The RunEngine Event Loop interferes with the qt event loop. Here we
    # kick it to keep it going.
    _draw_all()

    qApp.processEvents()
    loop.call_later(0.1, _qt_kicker)

loop = asyncio.get_event_loop()
loop.call_soon(_qt_kicker)
<commit_msg>API/MNT: make qt_kicker not install on import<commit_after>import sys

_INSTALLED = None

def install_qt_kicker():
    """Install a periodic callback to integrate qt and asyncio event loops

    If a version of the qt bindings are not already imported, this function
    will do nothing.

    It is safe to call this function multiple times.
    """

    global _INSTALLED
    if _INSTALLED is not None:
        return
    if not any(p in sys.modules for p in ['PyQt4', 'pyside', 'PyQt5']):
        return
    import asyncio
    import matplotlib.backends.backend_qt5
    from matplotlib.backends.backend_qt5 import _create_qApp
    from matplotlib._pylab_helpers import Gcf

    _create_qApp()
    qApp = matplotlib.backends.backend_qt5.qApp

    try:
        _draw_all = Gcf.draw_all  # mpl version >= 1.5
    except AttributeError:
        # slower, but backward-compatible
        def _draw_all():
            for f_mgr in Gcf.get_all_fig_managers():
                f_mgr.canvas.draw_idle()

    def _qt_kicker():
        # The RunEngine Event Loop interferes with the qt event loop. Here we
        # kick it to keep it going.
        _draw_all()

        qApp.processEvents()
        loop.call_later(0.03, _qt_kicker)

    loop = asyncio.get_event_loop()
    _INSTALLED = loop.call_soon(_qt_kicker)
<|endoftext|>


<commit_before>'''
Created on Sep 9, 2014

@author: noe
'''

import numpy as np
import pyemma.util.pystallone as stallone

class HiddenMSM(object):

    """
    Implements a discrete Hidden Markov state model of conformational kinetics.
    For details, see [1].

    [1]_ Noe, F. and Wu, H. and Prinz, J.-H. and Plattner, N. (2013)
    Projected and Hidden Markov Models for calculating kinetics and metastable states of complex molecules.
    J. Chem. Phys., 139 . p. 184114
    """

    def __init__(self, dtrajs, nstate, lag=1, conv=0.01, maxiter=None, timeshift=None):
        """
        dtrajs : int-array or list of int-arrays
            discrete trajectory or list of discrete trajectories
        nstate : int
            number of hidden states
        lag : int
            lag time at which the hidden transition matrix will be estimated
        conv = 0.01 : float
            convergence criterion. The EM optimization will stop when the likelihood has not increased by more
            than conv.
        maxiter : int
            maximum number of iterations until the EM optimization will be stopped even when no convergence
            is achieved. By default, will be set to 100 * nstate^2
        timeshift : int
            time-shift when using the window method for estimating at lag times > 1. For example, when we have
            lag = 10 and timeshift = 2, the estimation will be conducted using five subtrajectories with the
            following indexes:
            [0, 10, 20, ...]
            [2, 12, 22, ...]
            [4, 14, 24, ...]
            [6, 16, 26, ...]
            [8, 18, 28, ...]
            Basicly, when timeshift = 1, all data will be used, while for > 1 data will be subsampled. Setting
            timeshift greater than tau will have no effect, because at least the first subtrajectory will be
            used.
        """
        # format input data
        if (type(dtrajs) is np.ndarray):
            dtrajs = [dtrajs]
        sdtrajs = []
        # make a Java List
        for dtraj in dtrajs:
            sdtrajs.append(stallone.ndarray_to_stallone_array(dtraj))
        jlist = stallone.java.util.Arrays.asList(sdtrajs)
        # prepare run parameters
        # by default, use 10 shifts per lag, but at least 1
        timeshift = max(lag / 10, 1)
        if (maxiter is None):
            maxiter = 100 * nstate * nstate  # by default use 100 nstate^2
        # convergence when likelihood increases by no more than dlconv
        dectol = -conv
        # do not set initial values for hidden transition matrix or output
        # probabilities (will be obtained by PCCA+)
        TCinit = None
        chiInit = None
        # run estimation
        try:
            self.hmm = stallone.API.hmm.pmm(jlist, nstate, lag, timeshift,
                                            maxiter, dectol, TCinit, chiInit)
        except stallone.JavaException as je:
            raise RuntimeError('An error occurred during HMM estimation.'
                               ' Stack trace %s:\n' % je.stacktrace())

    @property
    def likelihood_history(self):
        """
        Returns the list of likelihood values encountered during the optimization
        """
        return np.array(self.hmm.getLogLikelihoodHistory())

    @property
    def niter(self):
        """
        Returns the number of EM optimization steps made
        """
        return np.shape(self.likelihood_history)[0]

    @property
    def transition_matrix(self):
        """
        Returns the hidden transition matrix
        """
        return stallone.stallone_array_to_ndarray(self.hmm.getTransitionMatrix())

    @property
    def output_matrix(self):
        """
        Returns the output probability matrix B, with b_ij
        containing the probability that hidden state i will output to observable state j
        """
        return stallone.stallone_array_to_ndarray(self.hmm.getOutputParameters())
<commit_msg>[hmm] convert lag time to int<commit_after>'''
Created on Sep 9, 2014

@author: noe
'''

import numpy as np
import pyemma.util.pystallone as stallone

class HiddenMSM(object):

    """
    Implements a discrete Hidden Markov state model of conformational kinetics.
    For details, see [1].

    [1]_ Noe, F. and Wu, H. and Prinz, J.-H. and Plattner, N. (2013)
    Projected and Hidden Markov Models for calculating kinetics and metastable states of complex molecules.
    J. Chem. Phys., 139 . p. 184114
    """

    def __init__(self, dtrajs, nstate, lag=1, conv=0.01, maxiter=None, timeshift=None):
        """
        dtrajs : int-array or list of int-arrays
            discrete trajectory or list of discrete trajectories
        nstate : int
            number of hidden states
        lag : int
            lag time at which the hidden transition matrix will be estimated
        conv = 0.01 : float
            convergence criterion. The EM optimization will stop when the likelihood has not increased by more
            than conv.
        maxiter : int
            maximum number of iterations until the EM optimization will be stopped even when no convergence
            is achieved. By default, will be set to 100 * nstate^2
        timeshift : int
            time-shift when using the window method for estimating at lag times > 1. For example, when we have
            lag = 10 and timeshift = 2, the estimation will be conducted using five subtrajectories with the
            following indexes:
            [0, 10, 20, ...]
            [2, 12, 22, ...]
            [4, 14, 24, ...]
            [6, 16, 26, ...]
            [8, 18, 28, ...]
            Basicly, when timeshift = 1, all data will be used, while for > 1 data will be subsampled. Setting
            timeshift greater than tau will have no effect, because at least the first subtrajectory will be
            used.
        """
        lag = int(lag)
        # format input data
        if (type(dtrajs) is np.ndarray):
            dtrajs = [dtrajs]
        sdtrajs = []
        # make a Java List
        for dtraj in dtrajs:
            sdtrajs.append(stallone.ndarray_to_stallone_array(dtraj))
        jlist = stallone.java.util.Arrays.asList(sdtrajs)
        # prepare run parameters
        # by default, use 10 shifts per lag, but at least 1
        timeshift = max(lag / 10, 1)
        if (maxiter is None):
            maxiter = 100 * nstate * nstate  # by default use 100 nstate^2
        # convergence when likelihood increases by no more than dlconv
        dectol = -conv
        # do not set initial values for hidden transition matrix or output
        # probabilities (will be obtained by PCCA+)
        TCinit = None
        chiInit = None
        # run estimation
        try:
            self.hmm = stallone.API.hmm.pmm(jlist, nstate, lag, timeshift,
                                            maxiter, dectol, TCinit, chiInit)
        except stallone.JavaException as je:
            raise RuntimeError('An error occurred during HMM estimation.'
                               ' Stack trace %s:\n' % je.stacktrace())

    @property
    def likelihood_history(self):
        """
        Returns the list of log likelihood values encountered during the optimization
        """
        return np.array(self.hmm.getLogLikelihoodHistory())

    @property
    def niter(self):
        """
        Returns the number of EM optimization steps made
        """
        return np.shape(self.likelihood_history)[0]

    @property
    def transition_matrix(self):
        """
        Returns the hidden transition matrix
        """
        return stallone.stallone_array_to_ndarray(self.hmm.getTransitionMatrix())

    @property
    def output_matrix(self):
        """
        Returns the output probability matrix B, with b_ij
        containing the probability that hidden state i will output to observable state j
        """
        return stallone.stallone_array_to_ndarray(self.hmm.getOutputParameters())
<|endoftext|>