<commit_before>import sysadmintoolkit

class Label(object):
    '''
    '''

    def __init__(self, label, plugin):
        '''
        label        str      Full command label, keywords separated by spaces.
                              Special characters:
                                  <keyword>    indicates this is a dynamic keyword.
                                  *            indicates that the rest of the label will
                                               not be parsed and will be passed straight
                                               to the function
        plugin      Plugin   Plugin instance
        '''
        # Validate label
        try:
            self.label = sysadmintoolkit.cmdprompt.CmdPrompt.merge_keywords(sysadmintoolkit.cmdprompt.CmdPrompt.split_label(label))
        except:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a string (1st arg)', errno=303)

        # Validated Plugin
        if isinstance(plugin, sysadmintoolkit.plugin.Plugin):
            self.plugin = plugin
        else:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a Plugin instance (2nd arg) for label "%s"' % label, errno=303)

        for reserved_character in sysadmintoolkit.cmdprompt.CmdPrompt.get_reserved_characters():
            if reserved_character in label:
                raise sysadmintoolkit.exception.PluginError('Error initializing command : Invalid character in label "%s"' % label, errno=303)

        # Modify to your own risk.
        # Reserved commands affect how the commandprompt behaves
        self.__is_reserved = False

    def get_label(self):
        '''
        '''
        return self.label

    def get_plugin(self):
        '''
        '''
        return self.plugin

    def is_reserved(self):
        '''
        '''
        return self.__is_reserved

class ExecCommand(Label):
    '''
    '''

    def __init__(self, label, plugin, function, allow_conflict=False):
        '''
        function     method   A method defined in the calling plugin.
                              def function(self, line, mode)
                              -> self will be plugin
                              -> Line entered at the cli, expanded
                              -> Command prompt Mode

        '''
        super(ExecCommand, self).__init__(label, plugin)

        # Validate function
        if type(function) is type(self.get_label):
            self.function = function
        else:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a function (3rd arg) for label "%s"' % label, errno=303)

    def get_function(self):
        '''
        '''
        return self.function

class LabelHelp(Label):
    '''
    '''

    def __init__(self, label, plugin, shorthelp):
        '''
        '''
        super(LabelHelp, self).__init__(label, plugin)

        self.shorthelp = shorthelp.strip().splitlines()[0]

    def get_shorthelp(self):
        '''
        '''
        return self.shorthelp
<commit_msg>* Added getshorthelp method<commit_after>import sysadmintoolkit

class Label(object):
    '''
    '''

    def __init__(self, label, plugin):
        '''
        label        str      Full command label, keywords separated by spaces.
                              Special characters:
                                  <keyword>    indicates this is a dynamic keyword.
                                  *            indicates that the rest of the label will
                                               not be parsed and will be passed straight
                                               to the function
        plugin      Plugin   Plugin instance
        '''
        # Validate label
        try:
            self.label = sysadmintoolkit.cmdprompt.CmdPrompt.merge_keywords(sysadmintoolkit.cmdprompt.CmdPrompt.split_label(label))
        except:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a string (1st arg)', errno=303)

        # Validated Plugin
        if isinstance(plugin, sysadmintoolkit.plugin.Plugin):
            self.plugin = plugin
        else:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a Plugin instance (2nd arg) for label "%s"' % label, errno=303)

        for reserved_character in sysadmintoolkit.cmdprompt.CmdPrompt.get_reserved_characters():
            if reserved_character in label:
                raise sysadmintoolkit.exception.PluginError('Error initializing command : Invalid character in label "%s"' % label, errno=303)

        # Modify to your own risk.
        # Reserved commands affect how the commandprompt behaves
        self.__is_reserved = False

    def get_label(self):
        '''
        '''
        return self.label

    def get_plugin(self):
        '''
        '''
        return self.plugin

    def is_reserved(self):
        '''
        '''
        return self.__is_reserved

class ExecCommand(Label):
    '''
    '''

    def __init__(self, label, plugin, function, allow_conflict=False):
        '''
        function     method   A method defined in the calling plugin.
                              def function(self, line, mode)
                              -> self will be plugin
                              -> Line entered at the cli, expanded
                              -> Command prompt Mode

        '''
        super(ExecCommand, self).__init__(label, plugin)

        # Validate function
        if type(function) is type(self.get_label):
            self.function = function
        else:
            raise sysadmintoolkit.exception.PluginError('Error initializing command : Command instance requires a function (3rd arg) for label "%s"' % label, errno=303)

    def get_function(self):
        '''
        '''
        return self.function

    def get_shorthelp(self):
        '''
        '''
        if self.function.__doc__ is not None:
            return self.function.__doc__.strip().splitlines()[0]
        else:
            return 'No help available'

class LabelHelp(Label):
    '''
    '''

    def __init__(self, label, plugin, shorthelp):
        '''
        '''
        super(LabelHelp, self).__init__(label, plugin)

        self.shorthelp = shorthelp.strip().splitlines()[0]

    def get_shorthelp(self):
        '''
        '''
        return self.shorthelp.strip().splitlines()[0]
<|endoftext|>


<commit_before>#!/usr/bin/python3

# written by sqall
# twitter: https://twitter.com/sqall01
# blog: https://h4des.org
# github: https://github.com/sqall01
#
# Licensed under the GNU Affero General Public License, version 3.

import sys
import logging
import optparse
import xml.etree.ElementTree
from typing import Tuple, List, Dict
from lib import Sqlite
from lib import GlobalData
from lib.localObjects import Alert, Sensor, AlertLevel, SensorDataType
from lib.config.parser import configure_alert_levels

try:
    import networkx as nx
    from networkx.drawing.nx_pydot import to_pydot
except Exception:
    print("Requires pip packages 'networkx' and 'pydot'. "
          + "Please install them by executing 'pip3 install networkx pydot'.")
    sys.exit(1)

class GraphAlert:

    def __init__(self, alert: Alert):
        self.alert = alert

    def __str__(self):
        temp = "\"Alert: " + self.alert.description + "\l" \
               + "Alert Id: " + str(self.alert.alertId) + "\l" \
               + "Remote Alert Id: " + str(self.alert.remoteAlertId) + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphAlert:
            return False
        return self.alert.alertId == other.alert.alertId

    def __hash__(self):
        return self.alert.alertId

class GraphAlertLevel:

    def __init__(self, alert_level: AlertLevel):
        self.alert_level = alert_level

    def __str__(self):
        temp = "\"AlertLevel: " + self.alert_level.name + "\l" \
               + "Level: " + str(self.alert_level.level) + "\l" \
               + "Trigger Always: " + str(self.alert_level.triggerAlways) + "\l" \
               + "Alert for Triggered: " + str(self.alert_level.triggerAlertTriggered) + "\l" \
               + "Alert for Normal: " + str(self.alert_level.triggerAlertNormal) + "\l" \
               + "Has Rules: " + str(self.alert_level.rulesActivated) + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphAlertLevel:
            return False
        return self.alert_level.level == other.alert_level.level

    def __hash__(self):
        return self.alert_level.level

class GraphSensor:

    def __init__(self, sensor: Sensor):
        self.sensor = sensor

    def __str__(self):
        data_type = "Unknown"
        if self.sensor.dataType == SensorDataType.NONE:
            data_type = "None"
        elif self.sensor.dataType == SensorDataType.INT:
            data_type = "Integer"
        elif self.sensor.dataType == SensorDataType.FLOAT:
            data_type = "Float"
        temp = "\"Sensor: " + self.sensor.description + "\l" \
               + "Sensor Id: " + str(self.sensor.sensorId) + "\l" \
               + "Remote Sensor Id: " + str(self.sensor.remoteSensorId) + "\l" \
               + "Alert Delay: " + str(self.sensor.alertDelay) + " sec\l" \
               + "Data Type: " + data_type + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphSensor:
            return False
        return self.sensor.sensorId == other.sensor.sensorId

    def __hash__(self):
        return self.sensor.sensorId

def create_graph(alert_levels: Dict[int, AlertLevel], alerts: List[Alert], sensors: List[Sensor]) -> nx.DiGraph:
    graph = nx.DiGraph()

    # Add alerts, alert levels, and sensors as node to the graph.
    for sensor_obj in sensors:
        graph.add_node(GraphSensor(sensor_obj))

    for alert_obj in alerts:
        graph.add_node(GraphAlert(alert_obj))

    for _, alert_level_obj in alert_levels.items():
        graph.add_node(GraphAlertLevel(alert_level_obj))

    # Connect alerts and sensors with alert levels.
    for alert_obj in alerts:
        for alert_level_int in alert_obj.alertLevels:
            graph.add_edge(GraphAlertLevel(alert_levels[alert_level_int]),
                           GraphAlert(alert_obj))

    for sensor_obj in sensors:
        for alert_level_int in sensor_obj.alertLevels:
            graph.add_edge(GraphSensor(sensor_obj),
                           GraphAlertLevel(alert_levels[alert_level_int]))

    return graph

def get_alerts_and_sensors(global_data: GlobalData) -> Tuple[List[Alert], List[Sensor]]:
    """
    Gets Alerts and Sensors from the database

    :return: List of Alert and List of Sensor objects
    """
    sys_info_list = global_data.storage.getAlertSystemInformation()
    sensor_objs = sys_info_list[2]
    alert_objs = sys_info_list[4]
    return alert_objs, sensor_objs

def write_graph(graph: nx.DiGraph, target_file, target_format="raw"):
    font = "Ubuntu Mono"
    dot = to_pydot(graph)
    for n in dot.get_node_list():
        n.set_fontname(font)
        n.set_shape("rect")
        if n.obj_dict["name"].startswith('"AlertLevel: '):
            n.set_fillcolor("#cecece")
            n.set_style("filled")
        elif n.obj_dict["name"].startswith('"Alert: '):
            n.set_fillcolor("#4e96d5")
            n.set_style("filled")
        elif n.obj_dict["name"].startswith('"Sensor: '):
            n.set_fillcolor("#25d426")
            n.set_style("filled")

    dot.write(target_file, format=target_format)

if __name__ == '__main__':

    # Parsing command line options.
    parser = optparse.OptionParser()
    parser.formatter = optparse.TitledHelpFormatter()
    parser.description = "Exports a graph containing Alerts, Alert Levels, and Sensors of your AlertR system."
    parser.epilog = "Example command create graph: " \
                    + "\t\t\t\t\t\t\t\t\t\t" \
                    + "'python3 %s -g /home/alertr/graph.dot'" % sys.argv[0]
    parser.add_option("-g",
                      "--graph",
                      dest="graph_path",
                      action="store",
                      help="Target graph location.",
                      default="")
    parser.add_option("",
                      "--png",
                      dest="png",
                      action="store_true",
                      help="Output PNG file.",
                      default=False)

    (options, args) = parser.parse_args()

    if options.graph_path == "":
        print("Use --help to get all available options.")
        sys.exit(0)

    # Create correct file ending and check target file writable.
    target_location = options.graph_path
    do_png = options.png
    target_format = "raw"
    if do_png:
        target_format = "png"
        if target_location[-4:] != ".png":
            target_location += ".png"
    else:
        if target_location[-4:] != ".dot":
            target_location += ".dot"
    try:
        with open(target_location, 'w') as fp:
            pass
    except Exception:
        print("Not able to write '%s'." % target_location)
        sys.exit(1)

    global_data = GlobalData()

    # Initialize logging.
    logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s',
                        datefmt='%m/%d/%Y %H:%M:%S',
                        level=logging.DEBUG)
    global_data.logger = logging.getLogger("graph")

    global_data.storage = Sqlite(global_data.storageBackendSqliteFile,
                                 global_data,
                                 read_only=True)

    # Read necessary configurations.
    configRoot = xml.etree.ElementTree.parse(global_data.configFile).getroot()
    configure_alert_levels(configRoot, global_data)

    # Get Alert, Alert Level, ans Sensor objects.
    alert_level_objs_dict = dict()
    for alert_level in global_data.alertLevels:
        alert_level_objs_dict[alert_level.level] = alert_level

    alert_objs, sensor_objs = get_alerts_and_sensors(global_data)

    # Create graph and export.
    graph = create_graph(alert_level_objs_dict, alert_objs, sensor_objs)
    write_graph(graph, target_location, target_format)
<commit_msg>added option for specific alert levels<commit_after>#!/usr/bin/python3

# written by sqall
# twitter: https://twitter.com/sqall01
# blog: https://h4des.org
# github: https://github.com/sqall01
#
# Licensed under the GNU Affero General Public License, version 3.

import sys
import logging
import optparse
import xml.etree.ElementTree
from typing import Tuple, List, Dict, Optional
from lib import Sqlite
from lib import GlobalData
from lib.localObjects import Alert, Sensor, AlertLevel, SensorDataType
from lib.config.parser import configure_alert_levels

try:
    import networkx as nx
    from networkx.drawing.nx_pydot import to_pydot
except Exception:
    print("Requires pip packages 'networkx' and 'pydot'. "
          + "Please install them by executing 'pip3 install networkx pydot'.")
    sys.exit(1)

class GraphAlert:

    def __init__(self, alert: Alert):
        self.alert = alert

    def __str__(self):
        temp = "\"Alert: " + self.alert.description + "\l" \
               + "Alert Id: " + str(self.alert.alertId) + "\l" \
               + "Remote Alert Id: " + str(self.alert.remoteAlertId) + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphAlert:
            return False
        return self.alert.alertId == other.alert.alertId

    def __hash__(self):
        return self.alert.alertId

class GraphAlertLevel:

    def __init__(self, alert_level: AlertLevel):
        self.alert_level = alert_level

    def __str__(self):
        temp = "\"AlertLevel: " + self.alert_level.name + "\l" \
               + "Level: " + str(self.alert_level.level) + "\l" \
               + "Trigger Always: " + str(self.alert_level.triggerAlways) + "\l" \
               + "Alert for Triggered: " + str(self.alert_level.triggerAlertTriggered) + "\l" \
               + "Alert for Normal: " + str(self.alert_level.triggerAlertNormal) + "\l" \
               + "Has Rules: " + str(self.alert_level.rulesActivated) + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphAlertLevel:
            return False
        return self.alert_level.level == other.alert_level.level

    def __hash__(self):
        return self.alert_level.level

class GraphSensor:

    def __init__(self, sensor: Sensor):
        self.sensor = sensor

    def __str__(self):
        data_type = "Unknown"
        if self.sensor.dataType == SensorDataType.NONE:
            data_type = "None"
        elif self.sensor.dataType == SensorDataType.INT:
            data_type = "Integer"
        elif self.sensor.dataType == SensorDataType.FLOAT:
            data_type = "Float"
        temp = "\"Sensor: " + self.sensor.description + "\l" \
               + "Sensor Id: " + str(self.sensor.sensorId) + "\l" \
               + "Remote Sensor Id: " + str(self.sensor.remoteSensorId) + "\l" \
               + "Alert Delay: " + str(self.sensor.alertDelay) + " sec\l" \
               + "Data Type: " + data_type + "\l\""
        return temp

    def __eq__(self, other):
        if type(other) != GraphSensor:
            return False
        return self.sensor.sensorId == other.sensor.sensorId

    def __hash__(self):
        return self.sensor.sensorId

def create_graph(alert_levels: Dict[int, AlertLevel], alerts: List[Alert], sensors: List[Sensor]) -> nx.DiGraph:
    graph = nx.DiGraph()

    # Add alerts, alert levels, and sensors as node to the graph.
    for sensor_obj in sensors:
        graph.add_node(GraphSensor(sensor_obj))

    for alert_obj in alerts:
        graph.add_node(GraphAlert(alert_obj))

    for _, alert_level_obj in alert_levels.items():
        graph.add_node(GraphAlertLevel(alert_level_obj))

    # Connect alerts and sensors with alert levels.
    for alert_obj in alerts:
        for alert_level_int in alert_obj.alertLevels:
            graph.add_edge(GraphAlertLevel(alert_levels[alert_level_int]),
                           GraphAlert(alert_obj))

    for sensor_obj in sensors:
        for alert_level_int in sensor_obj.alertLevels:
            graph.add_edge(GraphSensor(sensor_obj),
                           GraphAlertLevel(alert_levels[alert_level_int]))

    return graph

def get_alerts_and_sensors(global_data: GlobalData,
                           target_alert_level: Optional[int]) -> Tuple[List[Alert], List[Sensor]]:
    """
    Gets Alerts and Sensors from the database

    :return: List of Alert and List of Sensor objects
    """
    sys_info_list = global_data.storage.getAlertSystemInformation()

    # Filter Sensor objects if necessary.
    sensor_objs = []
    temp_sensor_objs = sys_info_list[2]
    if target_alert_level is not None:
        for sensor_obj in temp_sensor_objs:
            if target_alert_level in sensor_obj.alertLevels:
                sensor_obj.alertLevels = [target_alert_level]
                sensor_objs.append(sensor_obj)
    else:
        sensor_objs = temp_sensor_objs

    # Filter Alert objects if necessary.
    alert_objs = []
    temp_alert_objs = sys_info_list[4]
    if target_alert_level is not None:
        for alert_obj in temp_alert_objs:
            if target_alert_level in alert_obj.alertLevels:
                alert_obj.alertLevels = [target_alert_level]
                alert_objs.append(alert_obj)
    else:
        alert_objs = temp_alert_objs

    return alert_objs, sensor_objs

def write_graph(graph: nx.DiGraph, target_file, target_format="raw"):
    font = "Ubuntu Mono"
    dot = to_pydot(graph)
    for n in dot.get_node_list():
        n.set_fontname(font)
        n.set_shape("rect")
        if n.obj_dict["name"].startswith('"AlertLevel: '):
            n.set_fillcolor("#cecece")
            n.set_style("filled")
        elif n.obj_dict["name"].startswith('"Alert: '):
            n.set_fillcolor("#4e96d5")
            n.set_style("filled")
        elif n.obj_dict["name"].startswith('"Sensor: '):
            n.set_fillcolor("#25d426")
            n.set_style("filled")

    dot.write(target_file, format=target_format)

if __name__ == '__main__':

    # Parsing command line options.
    parser = optparse.OptionParser()
    parser.formatter = optparse.TitledHelpFormatter()
    parser.description = "Exports a graph containing Alerts, Alert Levels, and Sensors of your AlertR system."
    parser.epilog = "Example command create graph: " \
                    + "\t\t\t\t\t\t\t\t\t\t" \
                    + "'python3 %s -g /home/alertr/graph.dot'" % sys.argv[0] \
                    + "\t\t\t\t\t\t\t\t\t\t" \
                    + "Example command to create graph for a specific Alert Level:" \
                    + "\t\t\t\t\t\t\t\t\t\t" \
                    + "'python3 %s -g /home/alertr/graph.dot' -a 3" % sys.argv[0]
    parser.add_option("-g",
                      "--graph",
                      dest="graph_path",
                      action="store",
                      help="Target graph location. (Required)",
                      default="")
    parser.add_option("",
                      "--png",
                      dest="png",
                      action="store_true",
                      help="Output PNG file. (Optional)",
                      default=False)
    parser.add_option("-a",
                      "--alertlevel",
                      dest="alert_level",
                      action="store",
                      type="int",
                      help="A specific Alert Level to export. If not set, all Alert Levels are exported. (Optional)",
                      default=None)

    (options, args) = parser.parse_args()

    if options.graph_path == "":
        print("Use --help to get all available options.")
        sys.exit(0)

    target_alert_level = options.alert_level

    # Create correct file ending and check target file writable.
    target_location = options.graph_path
    do_png = options.png
    target_format = "raw"
    if do_png:
        target_format = "png"
        if target_location[-4:] != ".png":
            target_location += ".png"
    else:
        if target_location[-4:] != ".dot":
            target_location += ".dot"
    try:
        with open(target_location, 'w') as fp:
            pass
    except Exception:
        print("Not able to write '%s'." % target_location)
        sys.exit(1)

    global_data = GlobalData()

    # Initialize logging.
    logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s',
                        datefmt='%m/%d/%Y %H:%M:%S',
                        level=logging.WARNING)
    global_data.logger = logging.getLogger("graph")

    global_data.storage = Sqlite(global_data.storageBackendSqliteFile,
                                 global_data,
                                 read_only=True)

    # Read necessary configurations.
    configRoot = xml.etree.ElementTree.parse(global_data.configFile).getroot()
    configure_alert_levels(configRoot, global_data)

    # Get Alert, Alert Level, ans Sensor objects.
    alert_level_objs_dict = dict()
    for alert_level in global_data.alertLevels:
        if target_alert_level is None or alert_level.level == target_alert_level:
            alert_level_objs_dict[alert_level.level] = alert_level

    alert_objs, sensor_objs = get_alerts_and_sensors(global_data, target_alert_level)

    # Create graph and export.
    graph = create_graph(alert_level_objs_dict, alert_objs, sensor_objs)
    write_graph(graph, target_location, target_format)
    print("Graph written to '%s'." % target_location)
<|endoftext|>


<commit_before>
CONFIG = {
    'mode': 'wsgi',
    'working_dir': '/path/to/my/app',
    'python': '/usr/bin/python',
    'args': (
        '--bind=127.0.0.1:8080',
        '--workers=16',
        '--timeout=60',
        'app.module',
    ),
}
import urlparse
spisok=''

def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    resp = environ['QUERY_STRING'].split("&")
    print resp
    for arg in resp:
        response_body  = [arg+"\r\n"]

return ["response_body"]
<commit_msg>Update simple.py<commit_after>
CONFIG = {
    'mode': 'wsgi',
    'working_dir': '/path/to/my/app',
    'python': '/usr/bin/python',
    'args': (
        '--bind=127.0.0.1:8080',
        '--workers=16',
        '--timeout=60',
        'app.module',
    ),
}
import urlparse
spisok=''

def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    resp = environ['QUERY_STRING'].split("&")
    print resp
    for arg in resp:
        response_body  = [arg+"\r\n"]

    return ["response_body"]
<|endoftext|>


<commit_before># Copyright 2013-2019 Tom Eulenfeld, MIT license
"""
rf Documentation
================

rf is a Python framework for receiver function analysis.
Read and write support of necessary metadata is provided for
SAC, SeismicHandler and HDF5 waveform files.
Data is handled by the ``RFStream`` class which inherits a lot of useful
methods from its ObsPy ancestor ``Stream``,
but also has some unique methods necessary for receiver function calculation.

Method
------

The receiver function method is a popular technique to investigate crustal and
upper mantle velocity discontinuities. Basic concept of the method is that a
small part of incident P-waves from a teleseismic event gets converted to
S-waves at significant discontinuities under the receiver
(for P-receiver functions).
These converted Ps phases arrive at the station after the main P phase.
The response function of the receiver side (receiver function) is constructed
by removing the source and deep mantle propagation effects.
Firstly, the S-wave field is separated from the P-wave field by a rotation
from the station coordinate system (ZNE - vertical, north, east)
to the wave coordinate system (LQT - P-wave polarization,
approx. SV-wave polarization, SH-wave polarization).
Secondly, the waveform on the L component is deconvolved from the other
components, which removes source side and propagation effects.
The resulting functions are the Q and T component of the P receiver function.
Multiple reflected waves are also visible in the receiver function.
The conversion points of the rays are called piercing points.

For a more detailed description of the working flow see e.g. chapter 4.1 of
this_ dissertation.

.. image:: _static/2layer_rays.svg
   :height: 250px
   :alt: Ray paths

.. image:: _static/2layer_synrf.svg
   :height: 250px
   :alt: Synthetic receiver function

| *Left*: In a two-layer-model part of the incoming P-wave is converted to a
    S-wave at the layer boundary. Major multiples are Pppp, Ppps and Ppss.
| *Right*: Synthetic receiver function of Q component in a two-layer-model.

Installation
------------

Dependencies of rf are

    * ObsPy_ and some of its dependencies,
    * cartopy, geographiclib, shapely,
    * toeplitz_ for faster time domain deconvolution (optional),
    * obspyh5_ for hdf5 file support (optional),
    * tqdm for progress bar in batch processing (optional).

rf can be installed with ::

    pip install rf

The tests can be run with the script ::

    rf-runtests

To install the development version of rf download the source code and run ::

    pip install .

Here are some instructions to install rf into a fresh conda environment::

    conda config --add channels conda-forge
    conda create -n rfenv decorator matplotlib numpy scipy obspy tqdm shapely cartopy geographiclib h5py gfortran_linux-64
    conda activate rfenv
    pip install obspyh5 toeplitz rf
    rf-runtests

For OSX the package ``gfortran_osx-64`` must be used instead of ``gfortran_linux-64``.
For Windows you need to install the FORTRAN compiler yourself (MinGW).

Using the Python module
-----------------------

The main functionality is provided by the class `.RFStream`
which is derived from ObsPy's `~obspy.core.stream.Stream` class.

The canonical way to load a waveform file into a RFStream is to use
the `.read_rf()` function.

>>> from rf import read_rf
>>> stream = read_rf('myfile.SAC')

If you already have an ObsPy Stream and you want to turn it into a RFStream
use the generator of RFStream:

>>> from rf import RFStream
>>> stream = RFStream(obspy_stream)

The stream is again written to disc as usual by its write method:

>>> stream.write('outfile', 'SAC')

The RFStream object inherits a lot of useful methods from its ObsPy ancestor
(e.g. filter, taper, simulate, ...).

The module automatically maps important (for rf calculation) header information
from the stats object attached to every trace to the format specific headers.
At the moment only SAC and SH/Q headers are supported. When initializing an
RFStream the header information in the format specific headers are written to
the stats object and before writing the information stored in the stats object
is written back to the format specific headers. In this way the important
header information is guaranteed to be saved in the waveform files.
The following table reflects the mapping:

=================  =========  ======
stats              SH/Q       SAC
=================  =========  ======
station_latitude   COMMENT    stla
station_longitude  COMMENT    stlo
station_elevation  COMMENT    stel
event_latitude     LAT        evla
event_longitude    LON        evlo
event_depth        DEPTH      evdp
event_magnitude    MAGNITUDE  mag
event_time         ORIGIN     o
onset              P-ONSET    a
type               COMMENT    kuser0
phase              COMMENT    kuser1
moveout            COMMENT    kuser2
distance           DISTANCE   gcarc
back_azimuth       AZIMUTH    baz
inclination        INCI       user0
slowness           SLOWNESS   user1
pp_latitude        COMMENT    user2
pp_longitude       COMMENT    user3
pp_depth           COMMENT    user4
box_pos            COMMENT    user5
box_length         COMMENT    user6
=================  =========  ======

.. note::
    Q-file header COMMENT is used for storing some information, because
    the Q format has a shortage of predefined headers.

.. note::
    Alternatively the hdf5 file format can be used. It is supported via the
    obspyh5 package. In this case all supported stats
    entries are automatically attached to the stored data. The plugin also
    saves entries not listed here (e.g. processing information).

The first task when calculating receiver functions is calculating some ray
specific values like azimuth and epicentral distance. An appropriate stats
dictionary can be calculated with `.rfstats()`:

>>> from rf import rfstats
>>> stats = rfstats(station=station, event=event, phase='P', dist_range=(30,90))
>>> for tr in stream:
>>>     tr.stats.update(stats)

or if the station and event information is already stored in the stats object:

>>> rfstats(stream)

A typical workflow for P receiver function calculation and writing looks like

>>> stream.filter('bandpass', freqmin=0.05, freqmax=1.)
>>> stream.rf()
>>> stream.write('rf', 'Q')

rf can also calculate S receiver functions:

>>> stream.rf(method='S')

When calling stream.rf the following operations are performed depending on
the given kwargs:

    * filtering
    * trimming data to window relative to onset
    * downsampling
    * rotation
    * deconvolution

Please see `.RFStream.rf()`
for a more detailed description.
RFStream provides the possibility to perform moveout correction,
piercing point calculation and profile stacking.

Command line tool for batch processing
--------------------------------------

The rf package provides a command line utility 'rf' which runs all the
necessary steps to perform receiver function calculation.
Use
``rf -h`` and ``rf {your_command} -h`` to discover the interface.
All you need is an inventory file (StationXML) and a file with events
(QuakeML) you want to analyze.

The command ::

    rf create

creates a :ref:`template configuration file <config_label>` in the current
directory. This file is in JSON format and well documented.
After adapting the file to your needs you can use the various
commands of rf to perform different tasks (e.g. receiver function
calculation, plotting).

To create the tutorial with a small included dataset and working configuration
you can use ::

    rf create --tutorial

Now start using rf ..., e.g. ::

    rf data calc myrf
    rf moveout myrf myrfmout
    rf plot myrfmout myrfplot
    rf --moveout-phase Psss moveout myrf myrfPsssmout

.. note::
    Development of the batch module has a lower priority than the Python API.

Miscellaneous
-------------

Please feel free to request features, report bugs or contribute code on
GitHub_. The code is continuously tested by travis-ci. The test status of this
version is |buildstatus|.

.. _this: http://www.diss.fu-berlin.de/diss/servlets/MCRFileNodeServlet/FUDISS_derivate_000000014929/dissertation_richter.pdf
.. _ObsPy: http://www.obspy.org/
.. _pip: http://www.pip-installer.org/
.. _obspyh5: https://github.com/trichter/obspyh5/
.. _toeplitz: https://github.com/trichter/toeplitz/
.. _GitHub: https://github.com/trichter/rf/
.. |buildstatus| image:: https://api.travis-ci.org/trichter/rf.png?
    branch=master
   :target: https://travis-ci.org/trichter/rf
"""

__version__ = '0.9.1-dev'

from rf.profile import get_profile_boxes
from rf.rfstream import read_rf, RFStream, rfstats
from rf.util import iter_event_data, IterMultipleComponents

if 'dev' not in __version__:  # get image for correct version from travis-ci
    __doc__ = __doc__.replace('branch=master', 'branch=v%s' % __version__)
<commit_msg>add note about numpy flag<commit_after># Copyright 2013-2019 Tom Eulenfeld, MIT license
"""
rf Documentation
================

rf is a Python framework for receiver function analysis.
Read and write support of necessary metadata is provided for
SAC, SeismicHandler and HDF5 waveform files.
Data is handled by the ``RFStream`` class which inherits a lot of useful
methods from its ObsPy ancestor ``Stream``,
but also has some unique methods necessary for receiver function calculation.

Method
------

The receiver function method is a popular technique to investigate crustal and
upper mantle velocity discontinuities. Basic concept of the method is that a
small part of incident P-waves from a teleseismic event gets converted to
S-waves at significant discontinuities under the receiver
(for P-receiver functions).
These converted Ps phases arrive at the station after the main P phase.
The response function of the receiver side (receiver function) is constructed
by removing the source and deep mantle propagation effects.
Firstly, the S-wave field is separated from the P-wave field by a rotation
from the station coordinate system (ZNE - vertical, north, east)
to the wave coordinate system (LQT - P-wave polarization,
approx. SV-wave polarization, SH-wave polarization).
Secondly, the waveform on the L component is deconvolved from the other
components, which removes source side and propagation effects.
The resulting functions are the Q and T component of the P receiver function.
Multiple reflected waves are also visible in the receiver function.
The conversion points of the rays are called piercing points.

For a more detailed description of the working flow see e.g. chapter 4.1 of
this_ dissertation.

.. image:: _static/2layer_rays.svg
   :height: 250px
   :alt: Ray paths

.. image:: _static/2layer_synrf.svg
   :height: 250px
   :alt: Synthetic receiver function

| *Left*: In a two-layer-model part of the incoming P-wave is converted to a
    S-wave at the layer boundary. Major multiples are Pppp, Ppps and Ppss.
| *Right*: Synthetic receiver function of Q component in a two-layer-model.

Installation
------------

Dependencies of rf are

    * ObsPy_ and some of its dependencies,
    * cartopy, geographiclib, shapely,
    * toeplitz_ for faster time domain deconvolution (optional),
    * obspyh5_ for hdf5 file support (optional),
    * tqdm for progress bar in batch processing (optional).

rf can be installed with ::

    pip install rf

The tests can be run with the script ::

    rf-runtests

To install the development version of rf download the source code and run ::

    pip install .

Here are some instructions to install rf into a fresh conda environment::

    conda config --add channels conda-forge
    conda create -n rfenv decorator matplotlib numpy scipy obspy tqdm shapely cartopy geographiclib h5py gfortran_linux-64
    conda activate rfenv
    pip install obspyh5 toeplitz rf
    rf-runtests

For OSX the package ``gfortran_osx-64`` must be used instead of ``gfortran_linux-64``.
For Windows you need to install the FORTRAN compiler yourself (MinGW). Often, problems
with the compilation of Fortran codes (toeplitz package) can be solved by setting
``export NPY_DISTUTILS_APPEND_FLAGS=1``.

Using the Python module
-----------------------

The main functionality is provided by the class `.RFStream`
which is derived from ObsPy's `~obspy.core.stream.Stream` class.

The canonical way to load a waveform file into a RFStream is to use
the `.read_rf()` function.

>>> from rf import read_rf
>>> stream = read_rf('myfile.SAC')

If you already have an ObsPy Stream and you want to turn it into a RFStream
use the generator of RFStream:

>>> from rf import RFStream
>>> stream = RFStream(obspy_stream)

The stream is again written to disc as usual by its write method:

>>> stream.write('outfile', 'SAC')

The RFStream object inherits a lot of useful methods from its ObsPy ancestor
(e.g. filter, taper, simulate, ...).

The module automatically maps important (for rf calculation) header information
from the stats object attached to every trace to the format specific headers.
At the moment only SAC and SH/Q headers are supported. When initializing an
RFStream the header information in the format specific headers are written to
the stats object and before writing the information stored in the stats object
is written back to the format specific headers. In this way the important
header information is guaranteed to be saved in the waveform files.
The following table reflects the mapping:

=================  =========  ======
stats              SH/Q       SAC
=================  =========  ======
station_latitude   COMMENT    stla
station_longitude  COMMENT    stlo
station_elevation  COMMENT    stel
event_latitude     LAT        evla
event_longitude    LON        evlo
event_depth        DEPTH      evdp
event_magnitude    MAGNITUDE  mag
event_time         ORIGIN     o
onset              P-ONSET    a
type               COMMENT    kuser0
phase              COMMENT    kuser1
moveout            COMMENT    kuser2
distance           DISTANCE   gcarc
back_azimuth       AZIMUTH    baz
inclination        INCI       user0
slowness           SLOWNESS   user1
pp_latitude        COMMENT    user2
pp_longitude       COMMENT    user3
pp_depth           COMMENT    user4
box_pos            COMMENT    user5
box_length         COMMENT    user6
=================  =========  ======

.. note::
    Q-file header COMMENT is used for storing some information, because
    the Q format has a shortage of predefined headers.

.. note::
    Alternatively the hdf5 file format can be used. It is supported via the
    obspyh5 package. In this case all supported stats
    entries are automatically attached to the stored data. The plugin also
    saves entries not listed here (e.g. processing information).

The first task when calculating receiver functions is calculating some ray
specific values like azimuth and epicentral distance. An appropriate stats
dictionary can be calculated with `.rfstats()`:

>>> from rf import rfstats
>>> stats = rfstats(station=station, event=event, phase='P', dist_range=(30,90))
>>> for tr in stream:
>>>     tr.stats.update(stats)

or if the station and event information is already stored in the stats object:

>>> rfstats(stream)

A typical workflow for P receiver function calculation and writing looks like

>>> stream.filter('bandpass', freqmin=0.05, freqmax=1.)
>>> stream.rf()
>>> stream.write('rf', 'Q')

rf can also calculate S receiver functions:

>>> stream.rf(method='S')

When calling stream.rf the following operations are performed depending on
the given kwargs:

    * filtering
    * trimming data to window relative to onset
    * downsampling
    * rotation
    * deconvolution

Please see `.RFStream.rf()`
for a more detailed description.
RFStream provides the possibility to perform moveout correction,
piercing point calculation and profile stacking.

Command line tool for batch processing
--------------------------------------

The rf package provides a command line utility 'rf' which runs all the
necessary steps to perform receiver function calculation.
Use
``rf -h`` and ``rf {your_command} -h`` to discover the interface.
All you need is an inventory file (StationXML) and a file with events
(QuakeML) you want to analyze.

The command ::

    rf create

creates a :ref:`template configuration file <config_label>` in the current
directory. This file is in JSON format and well documented.
After adapting the file to your needs you can use the various
commands of rf to perform different tasks (e.g. receiver function
calculation, plotting).

To create the tutorial with a small included dataset and working configuration
you can use ::

    rf create --tutorial

Now start using rf ..., e.g. ::

    rf data calc myrf
    rf moveout myrf myrfmout
    rf plot myrfmout myrfplot
    rf --moveout-phase Psss moveout myrf myrfPsssmout

.. note::
    Development of the batch module has a lower priority than the Python API.

Miscellaneous
-------------

Please feel free to request features, report bugs or contribute code on
GitHub_. The code is continuously tested by travis-ci. The test status of this
version is |buildstatus|.

.. _this: http://www.diss.fu-berlin.de/diss/servlets/MCRFileNodeServlet/FUDISS_derivate_000000014929/dissertation_richter.pdf
.. _ObsPy: http://www.obspy.org/
.. _pip: http://www.pip-installer.org/
.. _obspyh5: https://github.com/trichter/obspyh5/
.. _toeplitz: https://github.com/trichter/toeplitz/
.. _GitHub: https://github.com/trichter/rf/
.. |buildstatus| image:: https://api.travis-ci.org/trichter/rf.png?
    branch=master
   :target: https://travis-ci.org/trichter/rf
"""

__version__ = '0.9.1-dev'

from rf.profile import get_profile_boxes
from rf.rfstream import read_rf, RFStream, rfstats
from rf.util import iter_event_data, IterMultipleComponents

if 'dev' not in __version__:  # get image for correct version from travis-ci
    __doc__ = __doc__.replace('branch=master', 'branch=v%s' % __version__)
<|endoftext|>


<commit_before>from abc import ABC, abstractmethod
from itertools import islice
from operator import itemgetter
from threading import RLock
from typing import (
    TYPE_CHECKING,
    Dict,
    Iterable,
    List,
    NamedTuple,
    Optional,
    Sequence,
    Tuple,
    Union,
)

from ._ratio import ratio_resolve
from .align import Align
from .console import Console, ConsoleOptions, RenderableType, RenderResult
from .highlighter import ReprHighlighter
from .panel import Panel
from .pretty import Pretty
from .repr import rich_repr, RichReprResult
from .region import Region
from .segment import Segment
from .style import StyleType

if TYPE_CHECKING:
    from rich.tree import Tree

class LayoutRender(NamedTuple):
    """An individual layout render."""

    region: Region
    render: List[List[Segment]]

RegionMap = Dict["Layout", Region]
RenderMap = Dict["Layout", LayoutRender]

class LayoutError(Exception):
    """Layout related error."""

class NoSplitter(LayoutError):
    """Requested splitter does not exist."""

class _Placeholder:
    """An internal renderable used as a Layout placeholder."""

    highlighter = ReprHighlighter()

    def __init__(self, layout: "Layout", style: StyleType = "") -> None:
        self.layout = layout
        self.style = style

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        width = options.max_width
        height = options.height or options.size.height
        layout = self.layout
        title = (
            f"{layout.name!r} ({width} x {height})"
            if layout.name
            else f"({width} x {height})"
        )
        yield Panel(
            Align.center(Pretty(layout), vertical="middle"),
            style=self.style,
            title=self.highlighter(title),
            border_style="blue",
        )

class Splitter(ABC):
    """Base class for a splitter."""

    name: str = ""

    @abstractmethod
    def get_tree_icon(self) -> str:
        """Get the icon (emoji) used in layout.tree"""

    @abstractmethod
    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        """Divide a region amongst several child layouts.

        Args:
            children (Sequence(Layout)): A number of child layouts.
            region (Region): A rectangular region to divide.
        """

class RowSplitter(Splitter):
    """Split a layout region in to rows."""

    name = "row"

    def get_tree_icon(self) -> str:
        return "[layout.tree.row]⬌"

    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        x, y, width, height = region
        render_widths = ratio_resolve(width, children)
        offset = 0
        _Region = Region
        for child, child_width in zip(children, render_widths):
            yield child, _Region(x + offset, y, child_width, height)
            offset += child_width

class ColumnSplitter(Splitter):
    """Split a layout region in to columns."""

    name = "column"

    def get_tree_icon(self) -> str:
        return "[layout.tree.column]⬍"

    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        x, y, width, height = region
        render_heights = ratio_resolve(height, children)
        offset = 0
        _Region = Region
        for child, child_height in zip(children, render_heights):
            yield child, _Region(x, y + offset, width, child_height)
            offset += child_height

@rich_repr
class Layout:
    """A renderable to divide a fixed height in to rows or columns.

    Args:
        renderable (RenderableType, optional): Renderable content, or None for placeholder. Defaults to None.
        name (str, optional): Optional identifier for Layout. Defaults to None.
        size (int, optional): Optional fixed size of layout. Defaults to None.
        minimum_size (int, optional): Minimum size of layout. Defaults to 1.
        ratio (int, optional): Optional ratio for flexible layout. Defaults to 1.
        visible (bool, optional): Visibility of layout. Defaults to True.
    """

    splitters = {"row": RowSplitter, "column": ColumnSplitter}

    def __init__(
        self,
        renderable: Optional[RenderableType] = None,
        *,
        name: Optional[str] = None,
        size: Optional[int] = None,
        minimum_size: int = 1,
        ratio: int = 1,
        visible: bool = True,
        height: Optional[int] = None,
    ) -> None:
        self._renderable = renderable or _Placeholder(self)
        self.size = size
        self.minimum_size = minimum_size
        self.ratio = ratio
        self.name = name
        self.visible = visible
        self.height = height
        self.splitter: Splitter = self.splitters["column"]()
        self._children: List[Layout] = []
        self._render_map: RenderMap = {}
        self._lock = RLock()

    def __rich_repr__(self) -> RichReprResult:
        yield "name", self.name, None
        yield "size", self.size, None
        yield "minimum_size", self.minimum_size, 1
        yield "ratio", self.ratio, 1

    @property
    def renderable(self) -> RenderableType:
        """Layout renderable."""
        return self if self._children else self._renderable

    @property
    def children(self) -> List["Layout"]:
        """Gets (visible) layout children."""
        return [child for child in self._children if child.visible]

    def get(self, name: str) -> Optional["Layout"]:
        """Get a named layout, or None if it doesn't exist.

        Args:
            name (str): Name of layout.

        Returns:
            Optional[Layout]: Layout instance or None if no layout was found.
        """
        if self.name == name:
            return self
        else:
            for child in self._children:
                named_layout = child.get(name)
                if named_layout is not None:
                    return named_layout
        return None

    def __getitem__(self, name: str) -> "Layout":
        layout = self.get(name)
        if layout is None:
            raise KeyError(f"No layout with name {name!r}")
        return layout

    @property
    def tree(self) -> "Tree":
        """Get a tree renderable to show layout structure."""
        from rich.styled import Styled
        from rich.table import Table
        from rich.tree import Tree

        def summary(layout: "Layout") -> Table:

            icon = layout.splitter.get_tree_icon()

            table = Table.grid(padding=(0, 1, 0, 0))

            text: RenderableType = (
                Pretty(layout) if layout.visible else Styled(Pretty(layout), "dim")
            )
            table.add_row(icon, text)
            _summary = table
            return _summary

        layout = self
        tree = Tree(
            summary(layout),
            guide_style=f"layout.tree.{layout.splitter.name}",
            highlight=True,
        )

        def recurse(tree: "Tree", layout: "Layout") -> None:
            for child in layout._children:
                recurse(
                    tree.add(
                        summary(child),
                        guide_style=f"layout.tree.{child.splitter.name}",
                    ),
                    child,
                )

        recurse(tree, self)
        return tree

    def split(
        self,
        *layouts: Union["Layout", RenderableType],
        splitter: Union[Splitter, str] = "column",
    ) -> None:
        """Split the layout in to multiple sub-layouts.

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
            splitter (Union[Splitter, str]): Splitter instance or name of splitter.
        """
        _layouts = [
            layout if isinstance(layout, Layout) else Layout(layout)
            for layout in layouts
        ]
        try:
            self.splitter = (
                splitter
                if isinstance(splitter, Splitter)
                else self.splitters[splitter]()
            )
        except KeyError:
            raise NoSplitter(f"No splitter called {splitter!r}")
        self._children[:] = _layouts

    def add_split(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Add a new layout(s) to existing split.

        Args:
            *layouts (Union[Layout, RenderableType]): Positional arguments should be renderables or (sub) Layout instances.

        """
        _layouts = (
            layout if isinstance(layout, Layout) else Layout(layout)
            for layout in layouts
        )
        self._children.extend(_layouts)

    def split_row(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Split the layout in tow a row (Layouts side by side).

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
        """
        self.split(*layouts, splitter="row")

    def split_column(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Split the layout in to a column (layouts stacked on top of each other).

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
        """
        self.split(*layouts, splitter="column")

    def unsplit(self) -> None:
        """Reset splits to initial state."""
        del self._children[:]

    def update(self, renderable: RenderableType) -> None:
        """Update renderable.

        Args:
            renderable (RenderableType): New renderable object.
        """
        with self._lock:
            self._renderable = renderable

    def refresh_screen(self, console: "Console", layout_name: str) -> None:
        """Refresh a sub-layout.

        Args:
            console (Console): Console instance where Layout is to be rendered.
            layout_name (str): Name of layout.
        """
        with self._lock:
            layout = self[layout_name]
            region, _lines = self._render_map[layout]
            (x, y, width, height) = region
            lines = console.render_lines(
                layout, console.options.update_dimensions(width, height)
            )
            self._render_map[layout] = LayoutRender(region, lines)
            console.update_screen_lines(lines, x, y)

    def _make_region_map(self, width: int, height: int) -> RegionMap:
        """Create a dict that maps layout on to Region."""
        stack: List[Tuple[Layout, Region]] = [(self, Region(0, 0, width, height))]
        push = stack.append
        pop = stack.pop
        layout_regions: List[Tuple[Layout, Region]] = []
        append_layout_region = layout_regions.append
        while stack:
            append_layout_region(pop())
            layout, region = layout_regions[-1]
            children = layout.children
            if children:
                for child_and_region in layout.splitter.divide(children, region):
                    push(child_and_region)

        region_map = {
            layout: region
            for layout, region in sorted(layout_regions, key=itemgetter(1))
        }
        return region_map

    def render(self, console: Console, options: ConsoleOptions) -> RenderMap:
        """Render the sub_layouts.

        Args:
            console (Console): Console instance.
            options (ConsoleOptions): Console options.

        Returns:
            RenderMap: A dict that maps Layout on to a tuple of Region, lines
        """
        render_width = options.max_width
        render_height = options.height or console.height
        region_map = self._make_region_map(render_width, render_height)
        layout_regions = [
            (layout, region)
            for layout, region in region_map.items()
            if not layout.children
        ]
        render_map: Dict["Layout", "LayoutRender"] = {}
        render_lines = console.render_lines
        update_dimensions = options.update_dimensions

        for layout, region in layout_regions:
            lines = render_lines(
                layout.renderable, update_dimensions(region.width, region.height)
            )
            render_map[layout] = LayoutRender(region, lines)
        return render_map

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        with self._lock:
            width = options.max_width or console.width
            height = options.height or console.height
            render_map = self.render(console, options.update_dimensions(width, height))
            self._render_map = render_map
            layout_lines: List[List[Segment]] = [[] for _ in range(height)]
            _islice = islice
            for (region, lines) in render_map.values():
                _x, y, _layout_width, layout_height = region
                for row, line in zip(
                    _islice(layout_lines, y, y + layout_height), lines
                ):
                    row.extend(line)

            new_line = Segment.line()
            for layout_row in layout_lines:
                yield from layout_row
                yield new_line

if __name__ == "__main__":
    from rich.console import Console
    from rich.panel import Panel

    console = Console()
    layout = Layout()

    layout.split_column(
        Layout(name="header", size=3),
        Layout(ratio=1, name="main"),
        Layout(size=10, name="footer"),
    )

    layout["main"].split_row(Layout(name="side"), Layout(name="body", ratio=2))

    layout["body"].split_row(Layout(name="content", ratio=2), Layout(name="s2"))

    layout["s2"].split_column(
        Layout(name="top"), Layout(name="middle"), Layout(name="bottom")
    )

    layout["side"].split_column(Layout(layout.tree, name="left1"), Layout(name="left2"))

    layout["content"].update("foo")

    console.print(layout)
<commit_msg>style: remove unused import<commit_after>from abc import ABC, abstractmethod
from itertools import islice
from operator import itemgetter
from threading import RLock
from typing import (
    TYPE_CHECKING,
    Dict,
    Iterable,
    List,
    NamedTuple,
    Optional,
    Sequence,
    Tuple,
    Union,
)

from ._ratio import ratio_resolve
from .align import Align
from .console import Console, ConsoleOptions, RenderableType, RenderResult
from .highlighter import ReprHighlighter
from .panel import Panel
from .pretty import Pretty
from .repr import rich_repr, RichReprResult
from .region import Region
from .segment import Segment
from .style import StyleType

if TYPE_CHECKING:
    from rich.tree import Tree

class LayoutRender(NamedTuple):
    """An individual layout render."""

    region: Region
    render: List[List[Segment]]

RegionMap = Dict["Layout", Region]
RenderMap = Dict["Layout", LayoutRender]

class LayoutError(Exception):
    """Layout related error."""

class NoSplitter(LayoutError):
    """Requested splitter does not exist."""

class _Placeholder:
    """An internal renderable used as a Layout placeholder."""

    highlighter = ReprHighlighter()

    def __init__(self, layout: "Layout", style: StyleType = "") -> None:
        self.layout = layout
        self.style = style

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        width = options.max_width
        height = options.height or options.size.height
        layout = self.layout
        title = (
            f"{layout.name!r} ({width} x {height})"
            if layout.name
            else f"({width} x {height})"
        )
        yield Panel(
            Align.center(Pretty(layout), vertical="middle"),
            style=self.style,
            title=self.highlighter(title),
            border_style="blue",
        )

class Splitter(ABC):
    """Base class for a splitter."""

    name: str = ""

    @abstractmethod
    def get_tree_icon(self) -> str:
        """Get the icon (emoji) used in layout.tree"""

    @abstractmethod
    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        """Divide a region amongst several child layouts.

        Args:
            children (Sequence(Layout)): A number of child layouts.
            region (Region): A rectangular region to divide.
        """

class RowSplitter(Splitter):
    """Split a layout region in to rows."""

    name = "row"

    def get_tree_icon(self) -> str:
        return "[layout.tree.row]⬌"

    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        x, y, width, height = region
        render_widths = ratio_resolve(width, children)
        offset = 0
        _Region = Region
        for child, child_width in zip(children, render_widths):
            yield child, _Region(x + offset, y, child_width, height)
            offset += child_width

class ColumnSplitter(Splitter):
    """Split a layout region in to columns."""

    name = "column"

    def get_tree_icon(self) -> str:
        return "[layout.tree.column]⬍"

    def divide(
        self, children: Sequence["Layout"], region: Region
    ) -> Iterable[Tuple["Layout", Region]]:
        x, y, width, height = region
        render_heights = ratio_resolve(height, children)
        offset = 0
        _Region = Region
        for child, child_height in zip(children, render_heights):
            yield child, _Region(x, y + offset, width, child_height)
            offset += child_height

@rich_repr
class Layout:
    """A renderable to divide a fixed height in to rows or columns.

    Args:
        renderable (RenderableType, optional): Renderable content, or None for placeholder. Defaults to None.
        name (str, optional): Optional identifier for Layout. Defaults to None.
        size (int, optional): Optional fixed size of layout. Defaults to None.
        minimum_size (int, optional): Minimum size of layout. Defaults to 1.
        ratio (int, optional): Optional ratio for flexible layout. Defaults to 1.
        visible (bool, optional): Visibility of layout. Defaults to True.
    """

    splitters = {"row": RowSplitter, "column": ColumnSplitter}

    def __init__(
        self,
        renderable: Optional[RenderableType] = None,
        *,
        name: Optional[str] = None,
        size: Optional[int] = None,
        minimum_size: int = 1,
        ratio: int = 1,
        visible: bool = True,
        height: Optional[int] = None,
    ) -> None:
        self._renderable = renderable or _Placeholder(self)
        self.size = size
        self.minimum_size = minimum_size
        self.ratio = ratio
        self.name = name
        self.visible = visible
        self.height = height
        self.splitter: Splitter = self.splitters["column"]()
        self._children: List[Layout] = []
        self._render_map: RenderMap = {}
        self._lock = RLock()

    def __rich_repr__(self) -> RichReprResult:
        yield "name", self.name, None
        yield "size", self.size, None
        yield "minimum_size", self.minimum_size, 1
        yield "ratio", self.ratio, 1

    @property
    def renderable(self) -> RenderableType:
        """Layout renderable."""
        return self if self._children else self._renderable

    @property
    def children(self) -> List["Layout"]:
        """Gets (visible) layout children."""
        return [child for child in self._children if child.visible]

    def get(self, name: str) -> Optional["Layout"]:
        """Get a named layout, or None if it doesn't exist.

        Args:
            name (str): Name of layout.

        Returns:
            Optional[Layout]: Layout instance or None if no layout was found.
        """
        if self.name == name:
            return self
        else:
            for child in self._children:
                named_layout = child.get(name)
                if named_layout is not None:
                    return named_layout
        return None

    def __getitem__(self, name: str) -> "Layout":
        layout = self.get(name)
        if layout is None:
            raise KeyError(f"No layout with name {name!r}")
        return layout

    @property
    def tree(self) -> "Tree":
        """Get a tree renderable to show layout structure."""
        from rich.styled import Styled
        from rich.table import Table
        from rich.tree import Tree

        def summary(layout: "Layout") -> Table:

            icon = layout.splitter.get_tree_icon()

            table = Table.grid(padding=(0, 1, 0, 0))

            text: RenderableType = (
                Pretty(layout) if layout.visible else Styled(Pretty(layout), "dim")
            )
            table.add_row(icon, text)
            _summary = table
            return _summary

        layout = self
        tree = Tree(
            summary(layout),
            guide_style=f"layout.tree.{layout.splitter.name}",
            highlight=True,
        )

        def recurse(tree: "Tree", layout: "Layout") -> None:
            for child in layout._children:
                recurse(
                    tree.add(
                        summary(child),
                        guide_style=f"layout.tree.{child.splitter.name}",
                    ),
                    child,
                )

        recurse(tree, self)
        return tree

    def split(
        self,
        *layouts: Union["Layout", RenderableType],
        splitter: Union[Splitter, str] = "column",
    ) -> None:
        """Split the layout in to multiple sub-layouts.

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
            splitter (Union[Splitter, str]): Splitter instance or name of splitter.
        """
        _layouts = [
            layout if isinstance(layout, Layout) else Layout(layout)
            for layout in layouts
        ]
        try:
            self.splitter = (
                splitter
                if isinstance(splitter, Splitter)
                else self.splitters[splitter]()
            )
        except KeyError:
            raise NoSplitter(f"No splitter called {splitter!r}")
        self._children[:] = _layouts

    def add_split(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Add a new layout(s) to existing split.

        Args:
            *layouts (Union[Layout, RenderableType]): Positional arguments should be renderables or (sub) Layout instances.

        """
        _layouts = (
            layout if isinstance(layout, Layout) else Layout(layout)
            for layout in layouts
        )
        self._children.extend(_layouts)

    def split_row(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Split the layout in tow a row (Layouts side by side).

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
        """
        self.split(*layouts, splitter="row")

    def split_column(self, *layouts: Union["Layout", RenderableType]) -> None:
        """Split the layout in to a column (layouts stacked on top of each other).

        Args:
            *layouts (Layout): Positional arguments should be (sub) Layout instances.
        """
        self.split(*layouts, splitter="column")

    def unsplit(self) -> None:
        """Reset splits to initial state."""
        del self._children[:]

    def update(self, renderable: RenderableType) -> None:
        """Update renderable.

        Args:
            renderable (RenderableType): New renderable object.
        """
        with self._lock:
            self._renderable = renderable

    def refresh_screen(self, console: "Console", layout_name: str) -> None:
        """Refresh a sub-layout.

        Args:
            console (Console): Console instance where Layout is to be rendered.
            layout_name (str): Name of layout.
        """
        with self._lock:
            layout = self[layout_name]
            region, _lines = self._render_map[layout]
            (x, y, width, height) = region
            lines = console.render_lines(
                layout, console.options.update_dimensions(width, height)
            )
            self._render_map[layout] = LayoutRender(region, lines)
            console.update_screen_lines(lines, x, y)

    def _make_region_map(self, width: int, height: int) -> RegionMap:
        """Create a dict that maps layout on to Region."""
        stack: List[Tuple[Layout, Region]] = [(self, Region(0, 0, width, height))]
        push = stack.append
        pop = stack.pop
        layout_regions: List[Tuple[Layout, Region]] = []
        append_layout_region = layout_regions.append
        while stack:
            append_layout_region(pop())
            layout, region = layout_regions[-1]
            children = layout.children
            if children:
                for child_and_region in layout.splitter.divide(children, region):
                    push(child_and_region)

        region_map = {
            layout: region
            for layout, region in sorted(layout_regions, key=itemgetter(1))
        }
        return region_map

    def render(self, console: Console, options: ConsoleOptions) -> RenderMap:
        """Render the sub_layouts.

        Args:
            console (Console): Console instance.
            options (ConsoleOptions): Console options.

        Returns:
            RenderMap: A dict that maps Layout on to a tuple of Region, lines
        """
        render_width = options.max_width
        render_height = options.height or console.height
        region_map = self._make_region_map(render_width, render_height)
        layout_regions = [
            (layout, region)
            for layout, region in region_map.items()
            if not layout.children
        ]
        render_map: Dict["Layout", "LayoutRender"] = {}
        render_lines = console.render_lines
        update_dimensions = options.update_dimensions

        for layout, region in layout_regions:
            lines = render_lines(
                layout.renderable, update_dimensions(region.width, region.height)
            )
            render_map[layout] = LayoutRender(region, lines)
        return render_map

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        with self._lock:
            width = options.max_width or console.width
            height = options.height or console.height
            render_map = self.render(console, options.update_dimensions(width, height))
            self._render_map = render_map
            layout_lines: List[List[Segment]] = [[] for _ in range(height)]
            _islice = islice
            for (region, lines) in render_map.values():
                _x, y, _layout_width, layout_height = region
                for row, line in zip(
                    _islice(layout_lines, y, y + layout_height), lines
                ):
                    row.extend(line)

            new_line = Segment.line()
            for layout_row in layout_lines:
                yield from layout_row
                yield new_line

if __name__ == "__main__":
    from rich.console import Console

    console = Console()
    layout = Layout()

    layout.split_column(
        Layout(name="header", size=3),
        Layout(ratio=1, name="main"),
        Layout(size=10, name="footer"),
    )

    layout["main"].split_row(Layout(name="side"), Layout(name="body", ratio=2))

    layout["body"].split_row(Layout(name="content", ratio=2), Layout(name="s2"))

    layout["s2"].split_column(
        Layout(name="top"), Layout(name="middle"), Layout(name="bottom")
    )

    layout["side"].split_column(Layout(layout.tree, name="left1"), Layout(name="left2"))

    layout["content"].update("foo")

    console.print(layout)
<|endoftext|>


<commit_before>
from .models import *
import os, sys
from .main import create_app
import argparse
import json
from sqlalchemy.sql import select

def _go(args):
    app = create_app(args)
    with app.app.app_context():

        def dump_event(e):
            return {
                'id': e.id,
                'type': e.type_id,
                'session_sequence': e.session_sequence_index,
                'time': str(e.client_time),
                'detail': json.loads(e.detail) if e.detail != '' else None,
            }

        def dump_task_start(e):
            d = dump_event(e)
            d['task_sequence'] = 0
            return d

        def dump_task_event(e):
            d = dump_event(e)
            d['task_sequence'] = e.task_event.task_sequence_index
            return d

        def dump_session(s):
            all_events = Event.query.filter_by(session_id=s.id).order_by(Event.id)

            non_task_events = []
            tasks = {}

            for e in all_events:
                if e.task_start is not None and e.task_event is None:
                    tasks[e.task_start.task_id] = {
                        'id': e.task_start.task_id,
                        'group': e.task_start.group_id,
                        'events': [dump_task_start(e)]
                    }

                elif e.task_event is not None:
                    tasks[e.task_event.task_id]['events'].append(dump_task_event(e))

                else:
                    non_task_events.append(dump_event(e))

            return {
                'id': s.id,
                'release': s.release_id,
                'time': str(s.client_time),
                'detail': json.loads(s.detail) if s.detail != '' else None,
                'tasks': [v for v in tasks.values()],
                'events': non_task_events
            }

        def dump_user(u):
            return {
                'id': u.id,
                'username': u.username,
                'sessions': [dump_session(s) for s in Session.query.filter_by(user_id=u.id)],
            }

        def dump_user_no_entry(uid):
            return {
                'id': uid,
                'sessions': [dump_session(s) for s in Session.query.filter_by(user_id=uid)],
            }

        if args.mode == 'user':
            users = [dump_user(u) for u in User.query.all()]
        elif args.mode == 'session':
            users = [dump_user_no_entry(u[0]) for u in db.session.query(Session.user_id).distinct()]
        else:
            raise ValueError("invalid mode!")

        print(json.dumps(users, indent=4))

def main():
    _desc = '''
    Dump data.
    '''
    parser = argparse.ArgumentParser(description=_desc)

    parser.add_argument('config', nargs='?', default=None, type=argparse.FileType('r'),
        help="Config file to use for the server. An alternative is to set the PAPIKA_CONFIG environment variable."
    )

    mut = parser.add_mutually_exclusive_group(required=True)
    mut.add_argument('-u', '--user', action='store_const', dest='mode', const='user',
        help="Use the user table to group and dump sessions. If you use the user table at all, this is probably what you want."
    )
    mut.add_argument('-s', '--session', action='store_const', dest='mode', const='session',
        help="Use the session table to group and dump sessions. If you do not use the user table, this option will instead select all sessions, grouping by user id."
    )

    _go(parser.parse_args())
<commit_msg>add filtering by release id to dump.py<commit_after>
from .models import *
import os, sys
from .main import create_app
import argparse
import json, uuid
from sqlalchemy.sql import select

def _go(args):
    valid_release_ids = None
    if args.releaseids is not None:
        valid_release_ids = frozenset([s.strip() for s in args.releaseids])
        args.releaseids.close()

    lst = [x for x in valid_release_ids]
    def do_dump_session(s):
        return valid_release_ids is None or s.release_id in valid_release_ids

    app = create_app(args)
    with app.app.app_context():

        def dump_event(e):
            return {
                'id': e.id,
                'type': e.type_id,
                'session_sequence': e.session_sequence_index,
                'time': str(e.client_time),
                'detail': json.loads(e.detail) if e.detail != '' else None,
            }

        def dump_task_start(e):
            d = dump_event(e)
            d['task_sequence'] = 0
            return d

        def dump_task_event(e):
            d = dump_event(e)
            d['task_sequence'] = e.task_event.task_sequence_index
            return d

        def dump_session(s):
            all_events = Event.query.filter_by(session_id=s.id).order_by(Event.id)

            non_task_events = []
            tasks = {}

            for e in all_events:
                if e.task_start is not None and e.task_event is None:
                    tasks[e.task_start.task_id] = {
                        'id': e.task_start.task_id,
                        'group': e.task_start.group_id,
                        'events': [dump_task_start(e)]
                    }

                elif e.task_event is not None:
                    tasks[e.task_event.task_id]['events'].append(dump_task_event(e))

                else:
                    non_task_events.append(dump_event(e))

            return {
                'id': s.id,
                'release': s.release_id,
                'time': str(s.client_time),
                'detail': json.loads(s.detail) if s.detail != '' else None,
                'tasks': [v for v in tasks.values()],
                'events': non_task_events
            }

        def dump_user(u):
            sessions = [dump_session(s) for s in Session.query.filter_by(user_id=u.id) if do_dump_session(s)]
            if len(sessions) == 0:
                return None
            else:
                return {
                    'id': u.id,
                    'username': u.username,
                    'sessions': sessions,
                }

        def dump_user_no_entry(uid):
            sessions = [dump_session(s) for s in Session.query.filter_by(user_id=uid) if do_dump_session(s)]
            if len(sessions) == 0:
                return None
            else:
                return {
                    'id': uid,
                    'sessions': sessions,
                }

        if args.mode == 'user':
            users = [dump_user(u) for u in User.query.all()]
        elif args.mode == 'session':
            users = [dump_user_no_entry(u[0]) for u in db.session.query(Session.user_id).distinct()]
        else:
            raise ValueError("invalid mode!")
        users = [x for x in users if x is not None]

        print(json.dumps(users, indent=4))

def main():
    _desc = '''
    Dump data.
    '''
    parser = argparse.ArgumentParser(description=_desc)

    parser.add_argument('config', nargs='?', default=None, type=argparse.FileType('r'),
        help="Config file to use for the server. An alternative is to set the PAPIKA_CONFIG environment variable."
    )

    mut = parser.add_mutually_exclusive_group(required=True)
    mut.add_argument('-u', '--user', action='store_const', dest='mode', const='user',
        help="Use the user table to group and dump sessions. If you use the user table at all, this is probably what you want."
    )
    mut.add_argument('-s', '--session', action='store_const', dest='mode', const='session',
        help="Use the session table to group and dump sessions. If you do not use the user table, this option will instead select all sessions, grouping by user id."
    )

    parser.add_argument('-r' '--release-id-file', default=None, dest='releaseids', type=argparse.FileType('r'),
        help="Restrict the dump to the release ids listed (one on each line) in the given file")

    _go(parser.parse_args())
<|endoftext|>