diff --git a/databroker/.doctr-files b/databroker/.doctr-files deleted file mode 100644 index 454e249b24..0000000000 --- a/databroker/.doctr-files +++ /dev/null @@ -1,252 +0,0 @@ -databroker/DBEP/DBEP01.html -databroker/DBEP/DBEP02.html -databroker/DBEP/index.html -databroker/DBEP/template.html -databroker/_images/ds_motor_plot.png -databroker/_modules/databroker/_drivers/jsonl.html -databroker/_modules/databroker/_drivers/mongo_embedded.html -databroker/_modules/databroker/_drivers/mongo_normalized.html -databroker/_modules/databroker/_drivers/msgpack.html -databroker/_modules/databroker/core.html -databroker/_modules/databroker/utils.html -databroker/_modules/databroker/v1.html -databroker/_modules/databroker/v2.html -databroker/_modules/index.html -databroker/_sources/DBEP/DBEP01.rst.txt -databroker/_sources/DBEP/DBEP02.rst.txt -databroker/_sources/DBEP/index.rst.txt -databroker/_sources/DBEP/template.rst.txt -databroker/_sources/data-access-overview.rst.txt -databroker/_sources/generated/databroker.Broker.__call__.rst.txt -databroker/_sources/generated/databroker.Broker.__getitem__.rst.txt -databroker/_sources/generated/databroker.Broker.add_filter.rst.txt -databroker/_sources/generated/databroker.Broker.alias.rst.txt -databroker/_sources/generated/databroker.Broker.clear_filters.rst.txt -databroker/_sources/generated/databroker.Broker.dynamic_alias.rst.txt -databroker/_sources/generated/databroker.Broker.export.rst.txt -databroker/_sources/generated/databroker.Broker.export_size.rst.txt -databroker/_sources/generated/databroker.Broker.fetch_external.rst.txt -databroker/_sources/generated/databroker.Broker.fill_event.rst.txt -databroker/_sources/generated/databroker.Broker.fill_events.rst.txt -databroker/_sources/generated/databroker.Broker.from_config.rst.txt -databroker/_sources/generated/databroker.Broker.fs.rst.txt -databroker/_sources/generated/databroker.Broker.get_config.rst.txt -databroker/_sources/generated/databroker.Broker.get_documents.rst.txt -databroker/_sources/generated/databroker.Broker.get_events.rst.txt -databroker/_sources/generated/databroker.Broker.get_fields.rst.txt -databroker/_sources/generated/databroker.Broker.get_images.rst.txt -databroker/_sources/generated/databroker.Broker.get_table.rst.txt -databroker/_sources/generated/databroker.Broker.insert.rst.txt -databroker/_sources/generated/databroker.Broker.name.rst.txt -databroker/_sources/generated/databroker.Broker.named.rst.txt -databroker/_sources/generated/databroker.Broker.process.rst.txt -databroker/_sources/generated/databroker.Broker.reg.rst.txt -databroker/_sources/generated/databroker.Broker.restream.rst.txt -databroker/_sources/generated/databroker.Broker.rst.txt -databroker/_sources/generated/databroker.Broker.stats.rst.txt -databroker/_sources/generated/databroker.Broker.stream.rst.txt -databroker/_sources/generated/databroker.Broker.stream_names_given_header.rst.txt -databroker/_sources/generated/databroker.Broker.v1.rst.txt -databroker/_sources/generated/databroker.Broker.v2.rst.txt -databroker/_sources/generated/databroker.Header.config_data.rst.txt -databroker/_sources/generated/databroker.Header.data.rst.txt -databroker/_sources/generated/databroker.Header.descriptors.rst.txt -databroker/_sources/generated/databroker.Header.devices.rst.txt -databroker/_sources/generated/databroker.Header.documents.rst.txt -databroker/_sources/generated/databroker.Header.events.rst.txt -databroker/_sources/generated/databroker.Header.fields.rst.txt -databroker/_sources/generated/databroker.Header.get.rst.txt -databroker/_sources/generated/databroker.Header.items.rst.txt -databroker/_sources/generated/databroker.Header.keys.rst.txt -databroker/_sources/generated/databroker.Header.rst.txt -databroker/_sources/generated/databroker.Header.start.rst.txt -databroker/_sources/generated/databroker.Header.stop.rst.txt -databroker/_sources/generated/databroker.Header.stream.rst.txt -databroker/_sources/generated/databroker.Header.stream_names.rst.txt -databroker/_sources/generated/databroker.Header.table.rst.txt -databroker/_sources/generated/databroker.Header.uid.rst.txt -databroker/_sources/generated/databroker.Header.values.rst.txt -databroker/_sources/generated/databroker.Header.xarray.rst.txt -databroker/_sources/generated/databroker.Header.xarray_dask.rst.txt -databroker/_sources/generated/databroker.list_configs.rst.txt -databroker/_sources/generated/databroker.lookup_config.rst.txt -databroker/_sources/generated/databroker.temp.rst.txt -databroker/_sources/generated/databroker.temp_config.rst.txt -databroker/_sources/generated/databroker.wrap_in_deprecated_doct.rst.txt -databroker/_sources/generated/databroker.wrap_in_doct.rst.txt -databroker/_sources/index.rst.txt -databroker/_sources/installation.rst.txt -databroker/_sources/overview.rst.txt -databroker/_sources/v1/api.rst.txt -databroker/_sources/v1/archiver.rst.txt -databroker/_sources/v1/configuration.rst.txt -databroker/_sources/v1/creating.rst.txt -databroker/_sources/v1/index.rst.txt -databroker/_sources/v1/tutorial.rst.txt -databroker/_sources/v2/administrator/index.rst.txt -databroker/_sources/v2/developer/index.rst.txt -databroker/_sources/v2/developer/reference.rst.txt -databroker/_sources/v2/index.rst.txt -databroker/_sources/v2/user/index.rst.txt -databroker/_sources/whats_new.rst.txt -databroker/_static/basic.css -databroker/_static/bnl-styles.css -databroker/_static/css/badge_only.css -databroker/_static/css/fonts/Roboto-Slab-Bold.woff -databroker/_static/css/fonts/Roboto-Slab-Bold.woff2 -databroker/_static/css/fonts/Roboto-Slab-Regular.woff -databroker/_static/css/fonts/Roboto-Slab-Regular.woff2 -databroker/_static/css/fonts/fontawesome-webfont.eot -databroker/_static/css/fonts/fontawesome-webfont.svg -databroker/_static/css/fonts/fontawesome-webfont.ttf -databroker/_static/css/fonts/fontawesome-webfont.woff -databroker/_static/css/fonts/fontawesome-webfont.woff2 -databroker/_static/css/fonts/lato-bold-italic.woff -databroker/_static/css/fonts/lato-bold-italic.woff2 -databroker/_static/css/fonts/lato-bold.woff -databroker/_static/css/fonts/lato-bold.woff2 -databroker/_static/css/fonts/lato-normal-italic.woff -databroker/_static/css/fonts/lato-normal-italic.woff2 -databroker/_static/css/fonts/lato-normal.woff -databroker/_static/css/fonts/lato-normal.woff2 -databroker/_static/css/theme.css -databroker/_static/doctools.js -databroker/_static/documentation_options.js -databroker/_static/file.png -databroker/_static/fonts/FontAwesome.otf -databroker/_static/fonts/Lato/lato-bold.eot -databroker/_static/fonts/Lato/lato-bold.ttf -databroker/_static/fonts/Lato/lato-bold.woff -databroker/_static/fonts/Lato/lato-bold.woff2 -databroker/_static/fonts/Lato/lato-bolditalic.eot -databroker/_static/fonts/Lato/lato-bolditalic.ttf -databroker/_static/fonts/Lato/lato-bolditalic.woff -databroker/_static/fonts/Lato/lato-bolditalic.woff2 -databroker/_static/fonts/Lato/lato-italic.eot -databroker/_static/fonts/Lato/lato-italic.ttf -databroker/_static/fonts/Lato/lato-italic.woff -databroker/_static/fonts/Lato/lato-italic.woff2 -databroker/_static/fonts/Lato/lato-regular.eot -databroker/_static/fonts/Lato/lato-regular.ttf -databroker/_static/fonts/Lato/lato-regular.woff -databroker/_static/fonts/Lato/lato-regular.woff2 -databroker/_static/fonts/Roboto-Slab-Bold.woff -databroker/_static/fonts/Roboto-Slab-Bold.woff2 -databroker/_static/fonts/Roboto-Slab-Light.woff -databroker/_static/fonts/Roboto-Slab-Light.woff2 -databroker/_static/fonts/Roboto-Slab-Regular.woff -databroker/_static/fonts/Roboto-Slab-Regular.woff2 -databroker/_static/fonts/Roboto-Slab-Thin.woff -databroker/_static/fonts/Roboto-Slab-Thin.woff2 -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff -databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 -databroker/_static/fonts/fontawesome-webfont.eot -databroker/_static/fonts/fontawesome-webfont.svg -databroker/_static/fonts/fontawesome-webfont.ttf -databroker/_static/fonts/fontawesome-webfont.woff -databroker/_static/fonts/fontawesome-webfont.woff2 -databroker/_static/fonts/lato-bold-italic.woff -databroker/_static/fonts/lato-bold-italic.woff2 -databroker/_static/fonts/lato-bold.woff -databroker/_static/fonts/lato-bold.woff2 -databroker/_static/fonts/lato-normal-italic.woff -databroker/_static/fonts/lato-normal-italic.woff2 -databroker/_static/fonts/lato-normal.woff -databroker/_static/fonts/lato-normal.woff2 -databroker/_static/jquery-3.5.1.js -databroker/_static/jquery.js -databroker/_static/js/badge_only.js -databroker/_static/js/html5shiv-printshiv.min.js -databroker/_static/js/html5shiv.min.js -databroker/_static/js/modernizr.min.js -databroker/_static/js/theme.js -databroker/_static/language_data.js -databroker/_static/minus.png -databroker/_static/plus.png -databroker/_static/pygments.css -databroker/_static/searchtools.js -databroker/_static/underscore-1.3.1.js -databroker/_static/underscore.js -databroker/data-access-overview.html -databroker/generated/databroker.Broker.__call__.html -databroker/generated/databroker.Broker.__getitem__.html -databroker/generated/databroker.Broker.add_filter.html -databroker/generated/databroker.Broker.alias.html -databroker/generated/databroker.Broker.clear_filters.html -databroker/generated/databroker.Broker.dynamic_alias.html -databroker/generated/databroker.Broker.export.html -databroker/generated/databroker.Broker.export_size.html -databroker/generated/databroker.Broker.fetch_external.html -databroker/generated/databroker.Broker.fill_event.html -databroker/generated/databroker.Broker.fill_events.html -databroker/generated/databroker.Broker.from_config.html -databroker/generated/databroker.Broker.fs.html -databroker/generated/databroker.Broker.get_config.html -databroker/generated/databroker.Broker.get_documents.html -databroker/generated/databroker.Broker.get_events.html -databroker/generated/databroker.Broker.get_fields.html -databroker/generated/databroker.Broker.get_images.html -databroker/generated/databroker.Broker.get_table.html -databroker/generated/databroker.Broker.html -databroker/generated/databroker.Broker.insert.html -databroker/generated/databroker.Broker.name.html -databroker/generated/databroker.Broker.named.html -databroker/generated/databroker.Broker.process.html -databroker/generated/databroker.Broker.reg.html -databroker/generated/databroker.Broker.restream.html -databroker/generated/databroker.Broker.stats.html -databroker/generated/databroker.Broker.stream.html -databroker/generated/databroker.Broker.stream_names_given_header.html -databroker/generated/databroker.Broker.v1.html -databroker/generated/databroker.Broker.v2.html -databroker/generated/databroker.Header.config_data.html -databroker/generated/databroker.Header.data.html -databroker/generated/databroker.Header.descriptors.html -databroker/generated/databroker.Header.devices.html -databroker/generated/databroker.Header.documents.html -databroker/generated/databroker.Header.events.html -databroker/generated/databroker.Header.fields.html -databroker/generated/databroker.Header.get.html -databroker/generated/databroker.Header.html -databroker/generated/databroker.Header.items.html -databroker/generated/databroker.Header.keys.html -databroker/generated/databroker.Header.start.html -databroker/generated/databroker.Header.stop.html -databroker/generated/databroker.Header.stream.html -databroker/generated/databroker.Header.stream_names.html -databroker/generated/databroker.Header.table.html -databroker/generated/databroker.Header.uid.html -databroker/generated/databroker.Header.values.html -databroker/generated/databroker.Header.xarray.html -databroker/generated/databroker.Header.xarray_dask.html -databroker/generated/databroker.list_configs.html -databroker/generated/databroker.lookup_config.html -databroker/generated/databroker.temp.html -databroker/generated/databroker.temp_config.html -databroker/generated/databroker.wrap_in_deprecated_doct.html -databroker/generated/databroker.wrap_in_doct.html -databroker/genindex.html -databroker/index.html -databroker/installation.html -databroker/objects.inv -databroker/overview.html -databroker/search.html -databroker/searchindex.js -databroker/v1/api.html -databroker/v1/archiver.html -databroker/v1/configuration.html -databroker/v1/creating.html -databroker/v1/index.html -databroker/v1/tutorial.html -databroker/v2/administrator/index.html -databroker/v2/developer/index.html -databroker/v2/developer/reference.html -databroker/v2/index.html -databroker/v2/user/index.html -databroker/whats_new.html \ No newline at end of file diff --git a/databroker/DBEP/DBEP01.html b/databroker/DBEP/DBEP01.html deleted file mode 100644 index e12099388a..0000000000 --- a/databroker/DBEP/DBEP01.html +++ /dev/null @@ -1,664 +0,0 @@ - - - - - - - - - - - Mobile files — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -
-

Mobile files

- -
-

Status

-

FEPs go through a number of phases in their lifetime:

-
    -
  • Partially Implemented: The FEP is being actively discussed, a sub set has been implemented.

  • -
-
- -
-

Abstract

-

This FEP adds the ability for filestore to copy / move files around -the file system and keep track of those changes.

-
-
-

Detailed description

-

This FEP will provide API to

-
-
    -
  • database to keep track of the full history of file locations implemented

  • -
  • make a copy of all data from a resource from one location in the file -system to another and update all relevant entries implemented

    -
      -
    • This may be trouble for some usage patterns where multiple -resources point to same file

    • -
    -
  • -
  • move files from one place to another implemented

  • -
  • delete files implemented

  • -
  • delete resources

  • -
  • verify data at both file system and Datum level

  • -
-
-
-
-

Implementation

-
-

General Requirements

-
-
    -
  • implement Datum-level hashing

    -
      -
    • this should be a new collection which is keyed on DatumID and -contains the hash (sha1 or md5) of the values

    • -
    • may contain additional statistics, proprieties about datum

      -
        -
      • shape, dtype, (min, max, mean, histogram ?)

      • -
      • may want to stats as separate transient DB

      • -
      -
    • -
    -
  • -
  • each file spec needs class/handler that will, given a resource, -produce a list of all the files that are needed partial, need to flesh out handlers

  • -
  • implement resource < - > absolute path mapping collection

    -
      -
    • this is transient as it can always be re-generated

    • -
    • need a way to flag as ‘alive’ or not

    • -
    -
  • -
  • implement hashing of files

  • -
  • maybe implement a chroot, as well as path into Resource implemented, but not as described

    -
      -
    • this is so that you can say change_root(resource_id, new_root) -and then the files along with the folder structure would be moved.

    • -
    • without doing this we could do something like -change_root(resource_id, n_base, new_root) where n_base is -how many layers of directory to strip off, but this requires -knowing a fair amount about the actually paths involved in the

    • -
    • Could also do something like change_path(path_mutation_func, -resource_id) where path_mutation_func is a str -> str -mapping function which is general, but is not great in terms of -keeping this a controlled process and puts a big burden on the -user.

    • -
    • if there are multiple copies of the same file be able to control -which version gets hit

      -
        -
      • this needs to be controllable based on which computer the compute -is running on

      • -
      -
    • -
    -
  • -
-
-
-
-

API proposal

-
-

Currently Implemented

-

Limited API

-
def change_root(resource, new_root, remove_origin=True, verify=False):
-    '''Change the root directory of a given resource
-
-    The registered handler must have a `get_file_list` method and the
-    process running this method must have read/write access to both the
-    source and destination file systems.
-
-
-     Parameters
-     ----------
-     resource_or_uid : Document or str
-         The resource to move the files of
-
-     new_root : str
-         The new 'root' to copy the files into
-
-     remove_origin : bool, optional (True)
-         If the source files should be removed
-
-     verify : bool, optional (False)
-         Verify that the move happened correctly.  This currently
-         is not implemented and will raise if ``verify == True``.
-    '''
-
- def shift_root(self, resource_or_uid, shift):
-     '''Shift directory levels between root and resource_path
-
-     This is useful because the root can be change via `change_root`.
-
-     Parameters
-     ----------
-     resource_or_uid : Document or str
-         The resource to change the root/resource_path allocation
-         of absolute path.
-
-     shift : int
-         The amount to shift the split.  Positive numbers move more
-         levels into the root and negative values move levels into
-         the resource_path
-
-     '''
-
-  def insert_resource(self, spec, resource_path, resource_kwargs, root=''):
-
-
-

additional public API draft:

-
def get_resources_by_root(root, partial=False):
-    pass
-
-
-def get_resources_by_path(path, partial=False):
-    pass
-
-
-def get_resources_by_spec(spec):
-    pass
-
-
-def get_resource_by_uid(uid):
-    pass
-
-
-

extended schema

-
resource_update = {
-    resource: uid,
-    old: original_resource_doc,
-    new: updated_serouce_doc,
-    time: timestamp (posix time),
-    cmd: str, the command that generated the insertion
-    cmd_kwargs: dict, the inputs to cmd
-    }
-
-resource = {
-     spec: str,
-     root: str,
-     resource_path: str,
-     resource_kwargs: dict,
-     uid: str
-     }
-
-
-
-
-

Full proposal

-

New python API

-
def copy_resource(resource_id, new_root, old_root=None):
-    """Copy all the files of a resource
-
-    Parameters
-    ----------
-    resource_id : uuid
-        The unique id of the resource to work on
-
-    new_root : str
-        The path to the location in the filesystem to cop
-        the files into.  The full existing directory structure
-        will be replicated on top of the now root
-
-    old_root : str, optional
-        If there exists more than one copy already, select
-        which one to use
-
-    """
-
-def move_resource(resource_id, old_root, new_root):
-    """Move all files for a resource to a new location
-
-
-    This is the same as copy then delete.  Because of the
-    delete step users must be explicit about source path.
-
-    Parameters
-    ----------
-    resource_id : uuid
-        The unique id of the resource to work on
-
-    old_root : str
-        If there exists more than one copy already, select
-        which one to use
-
-    new_root : str
-        The path to the location in the filesystem to cop
-        the files into.  The full existing directory structure
-        will be replicated on top of the now root
-
-    """
-
-def remove_resource(resource_id, old_root, force_last=False):
-    """Delete all files associated with a resource
-
-    Parameters
-    ----------
-    resource_id : uuid
-        The unique id of the resource to work on
-
-    old_root : str
-        Which set of files to delete
-
-    force_last : bool, optional
-        If False, will raise RuntimeError rather than
-        delete the last copy of the files.
-
-
-    """
-
-def insert_resource(spec, resource_root, resource_path, resource_kwargs=None):
-    """
-    Parameters
-    ----------
-
-    spec : str
-        spec used to determine what handler to use to open this
-        resource.
-
-    resource_path, resource_root : str or None
-        Url to the physical location of this resource
-
-    resource_kwargs : dict, optional
-        resource_kwargs name/value pairs of additional kwargs to be
-        passed to the handler to open this resource.
-
-    """
-
-def retrieve(eid, root_preference=None)
-    """
-    Given a resource identifier return the data.
-
-    The root_preference allows control over which copy
-    of the data is used if there is more than one available.
-
-    Parameters
-    ----------
-    eid : str
-        The resource ID (as stored in MDS)
-
-    root_preference : list, optional
-        A list of preferred root locations to pull data from in
-        descending order.
-
-        If None, fall back to configurable default.
-
-    Returns
-    -------
-    data : ndarray
-        The requested data as a numpy array
-    """
-
-
-

New DB schema:

-
class Resource(Document):
-    """
-
-    Parameters
-    ----------
-
-    spec : str
-        spec used to determine what handler to use to open this
-        resource.
-
-    resource_path : str
-        Url to the physical location of the resource
-
-    resource_kwargs : dict
-        name/value pairs of additional kwargs to be
-        passed to the handler to open this resource.
-
-    """
-
-    spec = StringField(required=True, unique=False)
-    path = StringField(required=True, unique=False)
-    kwargs = DictField(required=False)
-    uid = StringField(required=True, unique=True)
-
-    meta = {'indexes': ['-_id', 'resource_root'], 'db_alias': ALIAS}
-
-
-class ResourceRoots(DynamicDocument):
-    """
-    Many to one mapping between Resource documents and chroot paths.
-
-    The idea is that the absolute path of a file contains two
-    parts, the root, which is set by details of how the file
-    system is mounted, and the relative path which is set by some
-    sort of semantics.  For example in the path ::
-
-        /mnt/DATA/2015/05/06/my_data.h5
-
-    ``/mnt/DATA/`` is the root and ``2015/05/06/my_data.h5`` is
-    the relative path.
-
-    In the case of a URL this would be ::
-
-      http://data.nsls-ii.bnl.gov/xf11id/2015/05/06/my_data.h5
-
-    the root would be ``http://data.nsls-ii.bnl.gov/`` and the
-    relative path would be ``xf11id/2015/05/06/my_data.h5``
-
-    Parameters
-    ----------
-    root : str
-        The chroot of the resource.
-
-    resource_uid : str
-        The uid of the resource this is associated with
-
-    """
-    root = StringField(required=True, unique=False)
-    resource_uid = StringField(required=True, unique=False)
-
-
-class File(Document):
-    """
-    This is 'semi-transient', everything in here can be rebuilt
-    if needed from Resource, Datum, and their helper code, but
-    the hash can be used for validation
-    """
-    resource_uid = StringField(required=True, unique=False)
-    root = StringField(required=True, unique=False)
-
-    uid = StringField(required=True, unique=True)
-    abs_path = StringField(required=True, unique=True)
-    sha1_hash = StringField(required=True)
-    size = FloatField(required=True)
-    exists = Bool(required=True)
-
-
-class DatumStats(DynamicDocument):
-    datum_uid = StringField(required=True, unique=True)
-    sha1_hash = StringField(required=True)
-    shape = ListField(field=IntField())
-
-class CommandJournal(Document):
-    command = StringField(required=True)
-    args =  ListField()
-    kwargs = DictField()
-    success = Bool(required=True)
-
-
-

In a departure from our standard design protocol let File have the -‘exists’ field be updated. Or have a collection which is just a -(resource_uid, root) create/delete journal. Another option is to allow -remove to delete entries from File collection.

-
-
-
-
-

Backward compatibility

-

This will require a DB migration and breaks all of the AD instances that -insert into FS.

-
-
-

Alternatives

-

None yet

-
-
- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/DBEP/DBEP02.html b/databroker/DBEP/DBEP02.html deleted file mode 100644 index 1f83d52550..0000000000 --- a/databroker/DBEP/DBEP02.html +++ /dev/null @@ -1,450 +0,0 @@ - - - - - - - - - - - Promote Resource / Datum to first-class documents — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -
-

Promote Resource / Datum to first-class documents

- -
-

Status

-

Discussion

-
- -
-

Abstract

-

Currently Resource and Datum are directly inserted into the -AssetRegistry by ophyd. This breaks the document abstractions -by making a specific consumer ‘special’.

-
-
-

Detailed description

-

An odd asymmetry in how databroker works is that the documents for -HeaderSource and EventSource are emitted by the RunEngine and -can be subscribed to by one or more consumers. Each consumer is -notionally independent, each receive all of the documents, and do not -need to coordinate in any way (or even be aware of one another’s -existence). In contrast, the Resource and Datum documents are -inserted directly into an AssetRegistry by the ophyd objects. -This breaks the separation we have between the data collection process -/ hardware, the generation of the documents, and the consumers of -those documents and leads to several unfortunate situations:

-
-
    -
  • ohpyd objects hold an instance of an AssetRegisty

  • -
  • we need to keep track of which AssertRegistry things were -inserted into

  • -
  • consumers that want access to the asset documents need to also have -a handle to the database that the objects are inserting into

  • -
-
-

The proposed solution is to promote Resource and Datum documents -to be peer documents with Start, Stop, Descriptor and Event. -They will appear in the document stream and be inserted into -DataBroker via db.insert. This eliminates the ‘special’ -side-band communication and brings all consumers back to the same -footing. This will require coordinated changes to event-model, -databroker, bluesky, and ophyd.

-
-
-

Implementation

-

Currently, ophyd is responsible for collecting all of the values for -the Resource and Datum documents except for the uids. The uids -are generated by calls to reg.register_* and the datum uids are -subsequently returned to the RunEngine via obj.read. The -proposed change is:

-
-
    -
  1. ophyd objects would be responsible for generating the full Resource -and Datum documents and providing them to the RunEngine to be -emitted. ophyd may provide some helpers to make generating compliant -documents easy.

    -
      -
    1. Similar to the current documents, a Resource must be emitted -before any Datum that refers to it. A Datum can only refer -to a Resource that as been emitted after the most recent -Start and before the Stop for the most recent Start.

    2. -
    3. an identical (including uid) Resource and Datum maybe -emitted more than once, the consumers will need to handle this.

    4. -
    5. The Datum documents must be yielded only in the first -collect_asset_docs for which there UID is in read.

    6. -
    7. The Resource documents must only be yielded in the first -collect_asset_docs which includes a Datum that refers to -it.

    8. -
    9. Calls to read and collect_asset_docs must be -idempotent.

    10. -
    -

    Identical Resource and Datum documents are to support a single -Resource that may span many runs, such as background images, and -still ensure that with in the scope of a Start / Stop pair a -consumer will see all of the documents required.

    -
  2. -
  3. in save before the Event document is emitted the RunEngine -will acquire and emit any AssetRegistry Documents.

    -
      -
    1. in save the RunEngine knows what objects in the bundle, call -collect_asset_docs method

      -
      def collect_asset_docs(self) -> Iterator[Tuple[str, Dict[str, Any]]]:
      -    ...
      -
      -
      -

      which will yield the (name, doc) pairs for anything that -was just read.

      -
    2. -
    3. these documents will be emitted before the Event

    4. -
    -
  4. -
  5. consumers will now have access to all relevant documents and can -do what ever they want with them (insert into an asset registry, -live processing / display, copy files else where)

  6. -
-
-
-

event-model

-
-
    -
  1. add schema for Resource and Datum

  2. -
  3. assert that datum_id must be of the form {resource_id}/{N}. -This is required to support columnar stores where the Datum -documents are group by Resource id.

  4. -
-
-
-
-

databroker

-
-
    -
  1. teach insert how to deal with the additional documents.

  2. -
  3. revert API changes to use register_* which generate the uids.

  4. -
  5. helper tools for generating Resource and Datum documents -(maybe in ohpyd?)

  6. -
-
-
-
-

ophyd

-
-
    -
  1. implement new document generation methods on all devices that have -external data.

  2. -
-
-
-
-

bluesky

-
-
    -
  1. implement above logic in RunEngine._save

  2. -
-
-
-
-
-

Backward Compatibility

-

This will break all of the devices that currently use AssetRegistry, -however it will not change anything on the retrieve side. The -constraints on the datum_id can not be applied retro-actively, but -can be applied to all future data.

-

This excludes the option of having IOCs directly insert Resource and -Datum documents and expose datum_id values to the EPICS layer. We -only have one experimental use of this (GeRM caproto IOC). This level -of flexibility is not worth non-uniformity at the document level. If -we want to have the IOC generate all of the values (including the -uids), then they should expose those values to EPICs and the ophyd -object will only be responsible for marshaling those values.

-
-
-

Alternatives

-
-

Eliminate Resource and Datum as stand alone documents

-

An alternative considered was to eliminate the Resource and Datum -documents all together by merging Resource into Descriptor and -Datum into Event. However, this would break several long-standing -design principles:

-
-
    -
  • all values in ev['data'] are unstructured (scalar, strings, arrays)

  • -
  • Descriptors are immutable

  • -
-
-

In addition to breaking the insert side, this would also be a major -change on the retrieval side and would require maintaining either two -implementations forever or to migrate all existing data.

-

This would also require the ophyd objects having a way to notify the -RunEngine that it’s configuration / resource was stale so that the -Descriptor cache could be invalidated. (this is probably a good -idea anyway).

-

Despite being superficially simpler, the fallout from this alternative -would be far greater.

-
-
-
- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/DBEP/index.html b/databroker/DBEP/index.html deleted file mode 100644 index cab529ee6a..0000000000 --- a/databroker/DBEP/index.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - Databroker Enhancement Proposals — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- - - - -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/DBEP/template.html b/databroker/DBEP/template.html deleted file mode 100644 index 71d218f4d2..0000000000 --- a/databroker/DBEP/template.html +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - - - - - - DBEP Template — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -
-

DBEP Template

- -

This PEP template is a guideline of the sections that a DBEP should -contain. Extra sections may be added if appropriate, and unnecessary -sections may be noted as such.

-
-

Status

-

DBEPs go through a number of phases in their lifetime:

-
    -
  • Discussion: The DBEP is being actively discussed on the mailing -list and it is being improved by its author. The mailing list -discussion of the DBEP should include the DBEP number (DBEPxxx) in the -subject line so they can be easily related to the DBEP.

  • -
  • Progress: Consensus was reached on the mailing list and -implementation work has begun.

  • -
  • Completed: The implementation has been merged into master.

  • -
  • Superseded: This DBEP has been abandoned in favor of another -approach.

  • -
-
-
-

Branches and Pull requests

-

All development branches containing work on this DBEP should be linked to from here.

-

All pull requests submitted relating to this DBEP should be linked to -from here. (A DBEP does not need to be implemented in a single pull -request if it makes sense to implement it in discrete phases).

-
-
-

Abstract

-

The abstract should be a short description of what the DBEP will achieve.

-
-
-

Detailed description

-

This section describes the need for the DBEP. It should describe the -existing problem that it is trying to solve and why this DBEP makes the -situation better. It should include examples of how the new -functionality would be used and perhaps some use cases.

-
-
-

Implementation

-

This section lists the major steps required to implement the DBEP. -Where possible, it should be noted where one step is dependent on -another, and which steps may be optionally omitted. Where it makes -sense, each step should include a link related pull requests as the -implementation progresses.

-
-
-

Backward compatibility

-

This section describes the ways in which the DBEP breaks backward incompatibility.

-
-
-

Alternatives

-

If there were any alternative solutions to solving the same problem, -they should be discussed here, along with a justification for the -chosen approach.

-
-
- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_images/ds_motor_plot.png b/databroker/_images/ds_motor_plot.png deleted file mode 100644 index 651a7c9aaa..0000000000 Binary files a/databroker/_images/ds_motor_plot.png and /dev/null differ diff --git a/databroker/_modules/databroker/_drivers/jsonl.html b/databroker/_modules/databroker/_drivers/jsonl.html deleted file mode 100644 index f608fc2b58..0000000000 --- a/databroker/_modules/databroker/_drivers/jsonl.html +++ /dev/null @@ -1,461 +0,0 @@ - - - - - - - - - - - databroker._drivers.jsonl — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker._drivers.jsonl
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker._drivers.jsonl

-import errno
-import glob
-import json
-import os
-import pathlib
-import event_model
-
-from ..in_memory import BlueskyInMemoryCatalog
-from ..core import retry
-
-
-def gen(filename):
-    """
-    A JSONL file generator.
-
-    Parameters
-    ----------
-    filename: str
-        JSONL file to load.
-    """
-    with open(filename, 'r') as file:
-        for line in file:
-            name, doc = json.loads(line)
-            yield (name, doc)
-
-
-def get_stop(filename):
-    """
-    Returns the stop_doc of a Bluesky JSONL file.
-
-    The stop_doc is always the last line of the file.
-
-    Parameters
-    ----------
-    filename: str
-        JSONL file to load.
-    Returns
-    -------
-    stop_doc: dict or None
-        A Bluesky run_stop document or None if one is not present.
-    """
-    stop_doc = None
-    lastline, = tail(filename)
-    if lastline:
-        try:
-            name, doc = json.loads(lastline)
-        except json.JSONDecodeError:
-            ...
-            # stop_doc will stay None if it can't be decoded correctly.
-        else:
-            if (name == 'stop'):
-                stop_doc = doc
-    return stop_doc
-
-
-
[docs]class BlueskyJSONLCatalog(BlueskyInMemoryCatalog): - name = 'bluesky-jsonl-catalog' # noqa - - def __init__(self, paths, *, handler_registry=None, root_map=None, - filler_class=event_model.Filler, query=None, - transforms=None, **kwargs): - """ - This Catalog is backed by a newline-delimited JSON (jsonl) file. - - Each line of the file is expected to be a JSON list with two elements, - the document name (type) and the document itself. The documents are - expected to be in chronological order. - - Parameters - ---------- - paths : list - list of filepaths - handler_registry : dict, optional - This is passed to the Filler or whatever class is given in the - ``filler_class`` parameter below. - - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - - A 'handler class' may be any callable with the signature:: - - handler_class(resource_path, root, **resource_kwargs) - - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - - handler_instance(**datum_kwargs) - - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the - ``filler_class`` parameter below. - - str -> str mapping to account for temporarily moved/copied/remounted - files. Any resources which have a ``root`` in ``root_map`` will be - loaded using the mapped ``root``. - filler_class: type, optional - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the - same methods as ``DocumentRouter``. - query : dict, optional - Mongo query that filters entries' RunStart documents - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - # Tolerate a single path (as opposed to a list). - if isinstance(paths, (str, pathlib.Path)): - paths = [paths] - self.paths = paths - self._filename_to_mtime = {} - - super().__init__(handler_registry=handler_registry, - root_map=root_map, filler_class=filler_class, - query=query, transforms=transforms, **kwargs) - - def _load(self): - for path in self.paths: - for filename in glob.glob(path): - mtime = os.path.getmtime(filename) - if mtime == self._filename_to_mtime.get(filename): - # This file has not changed since last time we loaded it. - continue - self._filename_to_mtime[filename] = mtime - with open(filename, 'r') as file: - try: - name, start_doc = json.loads(file.readline()) - except json.JSONDecodeError as e: - if not file.readline(): - # Empty file, maybe being written to currently - continue - raise e - stop_doc = get_stop(filename) - self.upsert(start_doc, stop_doc, gen, (filename,), {}) - -
[docs] @retry - def search(self, query): - """ - Return a new Catalog with a subset of the entries in this Catalog. - - Parameters - ---------- - query : dict - """ - query = dict(query) - if self._query: - query = {'$and': [self._query, query]} - cat = type(self)( - paths=self.paths, - query=query, - handler_registry=self._handler_registry, - transforms=self._transforms, - root_map=self._root_map, - name='search results', - getenv=self.getenv, - getshell=self.getshell, - auth=self.auth, - metadata=(self.metadata or {}).copy(), - storage_options=self.storage_options) - return cat
- - def _get_serializer(self): - "This is used internally by v1.Broker. It may be removed in future." - from suitcase.jsonl import Serializer - from event_model import RunRouter - path, *_ = self.paths - directory = os.path.dirname(path) - - def factory(name, doc): - serializer = Serializer(directory) - return [serializer], [] - - return RunRouter([factory])
- - -def tail(filename, n=1, bsize=2048): - """ - Returns a generator with the last n lines of a file. - - Thanks to Martijn Pieters for this solution: - https://stackoverflow.com/a/12295054/6513183 - - Parameters - ---------- - filename : string - n: int - number of lines - bsize: int - seek step size - Returns - ------- - line : generator - """ - - # get newlines type, open in universal mode to find it - with open(filename, 'r', newline=None) as hfile: - if not hfile.readline(): - return # empty, no point - sep = hfile.newlines # After reading a line, python gives us this - assert isinstance(sep, str), 'multiple newline types found, aborting' - - # find a suitable seek position in binary mode - with open(filename, 'rb') as hfile: - hfile.seek(0, os.SEEK_END) - linecount = 0 - pos = 0 - - while linecount <= n + 1: - # read at least n lines + 1 more; we need to skip a partial line later on - try: - hfile.seek(-bsize, os.SEEK_CUR) # go backwards - linecount += hfile.read(bsize).count(sep.encode()) # count newlines - hfile.seek(-bsize, os.SEEK_CUR) # go back again - except IOError as e: - if e.errno == errno.EINVAL: - # Attempted to seek past the start, can't go further - bsize = hfile.tell() - hfile.seek(0, os.SEEK_SET) - pos = 0 - linecount += hfile.read(bsize).count(sep.encode()) - break - raise # Some other I/O exception, re-raise - pos = hfile.tell() - - # Re-open in text mode - with open(filename, 'r') as hfile: - hfile.seek(pos, os.SEEK_SET) # our file position from above - for line in hfile: - # We've located n lines *or more*, so skip if needed - if linecount > n: - linecount -= 1 - continue - # The rest we yield - yield line.rstrip() -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/_drivers/mongo_embedded.html b/databroker/_modules/databroker/_drivers/mongo_embedded.html deleted file mode 100644 index 15e9d10550..0000000000 --- a/databroker/_modules/databroker/_drivers/mongo_embedded.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - - databroker._drivers.mongo_embedded — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker._drivers.mongo_embedded
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker._drivers.mongo_embedded

-import collections.abc
-import event_model
-from sys import maxsize
-from functools import partial
-import logging
-import cachetools
-import pymongo
-import pymongo.errors
-
-from ..core import Entry
-from ..v2 import Broker
-
-
-logger = logging.getLogger(__name__)
-
-
-class _Entries(collections.abc.Mapping):
-    "Mock the dict interface around a MongoDB query result."
-    def __init__(self, catalog):
-        self.catalog = catalog
-        self.__cache = cachetools.LRUCache(1024)
-
-    def cache_clear(self):
-        self.__cache.clear()
-
-    def _doc_to_entry(self, run_start_doc):
-
-        header_doc = None
-        uid = run_start_doc['uid']
-
-        def get_header_field(field):
-            nonlocal header_doc
-            if header_doc is None:
-                header_doc = self.catalog._db.header.find_one(
-                                {'run_id': uid}, {'_id': False})
-            if field in header_doc:
-                if field in ['start', 'stop']:
-                    return header_doc[field][0]
-                else:
-                    return header_doc[field]
-            else:
-                if field == 'resources':
-                    return []
-                elif field[0:6] == 'count_':
-                    return 0
-                else:
-                    return None
-
-        def get_resource(uid):
-            resources = get_header_field('resources')
-            for resource in resources:
-                if resource['uid'] == uid:
-                    return resource
-            raise ValueError(f"Could not find Resource with uid={uid}")
-
-        def lookup_resource_for_datum(datum_id):
-            """ This method is likely very slow. """
-            resources = [resource['uid']
-                         for resource in get_header_field('resources')]
-            datum_page = self.catalog._db.datum.find_one(
-                    {'$and': [{'resource': {'$in': resources}},
-                              {'datum_id': datum_id}]},
-                    {'_id': False})
-            for datum in event_model.unpack_datum_page(datum_page):
-                if datum['datum_id'] == datum_id:
-                    return datum['resource']
-            raise ValueError(f"Could not find Datum with datum_id={datum_id}")
-
-        def get_run_start():
-            return run_start_doc
-
-        def get_event_count(descriptor_uid):
-            return get_header_field(f'count_{descriptor_uid}')
-
-        entry_metadata = {'start': get_header_field('start'),
-                          'stop': get_header_field('stop')}
-
-        args = dict(
-            get_run_start=get_run_start,
-            get_run_stop=partial(get_header_field, 'stop'),
-            get_event_descriptors=partial(get_header_field, 'descriptors'),
-            get_event_pages=self.catalog._get_event_pages,
-            get_event_count=get_event_count,
-            get_resource=get_resource,
-            get_resources=partial(get_header_field, 'resources'),
-            lookup_resource_for_datum=lookup_resource_for_datum,
-            get_datum_pages=self.catalog._get_datum_pages,
-            get_filler=self.catalog._get_filler,
-            transforms=self.catalog._transforms)
-        return Entry(
-            name=run_start_doc['uid'],
-            description={},  # TODO
-            driver='databroker.core.BlueskyRun',
-            direct_access='forbid',  # ???
-            args=args,
-            cache=None,  # ???
-            parameters=[],
-            metadata=entry_metadata,
-            catalog_dir=None,
-            getenv=True,
-            getshell=True,
-            catalog=self.catalog)
-
-    def __iter__(self):
-        cursor = self.catalog._db.header.find(
-                    self.catalog._query,
-                    {'start.uid': True, '_id': False},
-                    sort=[('start.time', pymongo.DESCENDING)])
-
-        for doc in cursor:
-            yield doc['start'][0]['uid']
-
-    def _find_header_doc(self, name):
-        # If this came from a client, we might be getting '-1'.
-        try:
-            N = int(name)
-        except ValueError:
-            query = {'$and': [self.catalog._query, {'uid': name}]}
-            header_doc = self.catalog._db.header.find_one(query)
-            if header_doc is None:
-                regex_query = {
-                    '$and': [self.catalog._query,
-                             {'start.uid': {'$regex': f'{name}.*'}}]}
-                matches = list(
-                    self.catalog._db.header.find(regex_query).limit(10))
-                if not matches:
-                    raise KeyError(name)
-                elif len(matches) == 1:
-                    header_doc, = matches
-                else:
-                    match_list = '\n'.join(doc['uid'] for doc in matches)
-                    raise ValueError(
-                        f"Multiple matches to partial uid {name!r}. "
-                        f"Up to 10 listed here:\n"
-                        f"{match_list}")
-        else:
-            if N < 0:
-                # Interpret negative N as "the Nth from last entry".
-                query = self.catalog._query
-                cursor = (self.catalog._db.header.find(query)
-                          .sort('start.time', pymongo.DESCENDING)
-                          .skip(-N - 1)
-                          .limit(1))
-                try:
-                    header_doc, = cursor
-                except ValueError:
-                    raise IndexError(
-                        f"Catalog only contains {len(self.catalog)} "
-                        f"runs.")
-            else:
-                # Interpret positive N as
-                # "most recent entry with scan_id == N".
-                query = {'$and': [self.catalog._query, {'start.scan_id': N}]}
-                cursor = (self.catalog._db.header.find(query)
-                          .sort('start.time', pymongo.DESCENDING)
-                          .limit(1))
-                try:
-                    header_doc, = cursor
-                except ValueError:
-                    raise KeyError(f"No run with scan_id={N}")
-        if header_doc is None:
-            raise KeyError(name)
-        return header_doc
-
-    def __getitem__(self, name):
-        header_doc = self._find_header_doc(name)
-        uid = header_doc['start'][0]['uid']
-        try:
-            entry = self.__cache[uid]
-            logger.debug('Mongo Entries cache found %r', uid)
-        except KeyError:
-            entry = self._doc_to_entry(header_doc['start'][0])
-            self.__cache[uid] = entry
-        # The user has requested one specific Entry. In order to give them a
-        # more useful object, 'get' the Entry for them. Note that if they are
-        # expecting an Entry and try to call ``()`` or ``.get()``, that will
-        # still work because BlueskyRun supports those methods and will just
-        # return itself.
-        return entry.get()  # an instance of BlueskyRun
-
-    def __contains__(self, key):
-        # Try the fast path first.
-        if key in self.__cache:
-            return True
-        # Avoid paying for creating the Entry yet. Do just enough work decide
-        # if we *can* create such an Entry.
-        try:
-            self._find_header_doc(key)
-        except KeyError:
-            return False
-        else:
-            return True
-
-    def __len__(self):
-        return len(self.catalog)
-
-
-
[docs]class BlueskyMongoCatalog(Broker): - def __init__(self, datastore_db, *, handler_registry=None, root_map=None, - filler_class=event_model.Filler, query=None, - transforms=None, **kwargs): - """ - This Catalog is backed by a MongoDB with an embedded data model. - - This embedded data model has three collections: header, event, datum. - The header collection includes start, stop, descriptor, and resource - documents. The event_pages are stored in the event colleciton, and - datum_pages are stored in the datum collection. - - Parameters - ---------- - datastore_db : pymongo.database.Database or string - Must be a Database or a URI string that includes a database name. - handler_registry : dict, optional - This is passed to the Filler or whatever class is given in the - filler_class parametr below. - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - A 'handler class' may be any callable with the signature:: - handler_class(resource_path, root, **resource_kwargs) - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - handler_instance(**datum_kwargs) - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the filler_class - parameter below. - str -> str mapping to account for temporarily moved/copied/remounted - files. Any resources which have a ``root`` in ``root_map`` will be - loaded using the mapped ``root``. - filler_class: type, optional - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the - same methods as ``DocumentRouter``. - query : dict, optional - MongoDB query. Used internally by the ``search()`` method. - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - name = 'bluesky-mongo-embedded-catalog' # noqa - - if isinstance(datastore_db, str): - self._db = _get_database(datastore_db) - else: - self._db = datastore_db - self._query = query or {} - self._root_map = root_map - self._filler_class = filler_class - - super().__init__(handler_registry=handler_registry, - root_map=root_map, filler_class=filler_class, - transforms=transforms, **kwargs) - - def _get_event_pages(self, descriptor_uid, skip=0, limit=None): - if limit is None: - limit = maxsize - - page_cursor = self._db.event.find( - {'$and': [ - {'descriptor': descriptor_uid}, - {'last_index': {'$gte': skip}}, - {'first_index': {'$lte': skip + limit}}]}, - {'_id': False}, - sort=[('last_index', pymongo.ASCENDING)]) - - return page_cursor - - def _get_datum_pages(self, resource_uid, skip=0, limit=None): - if limit is None: - limit = maxsize - - page_cursor = self._db.datum.find( - {'$and': [ - {'resource': resource_uid}, - {'last_index': {'$gte': skip}}, - {'first_index': {'$lte': skip + limit}}]}, - {'_id': False}, - sort=[('last_index', pymongo.ASCENDING)]) - - return page_cursor - - def _make_entries_container(self): - return _Entries(self) - - def _close(self): - self._client.close() - - def __len__(self): - return self._db.header.count_documents(self._query) - -
[docs] def search(self, query): - """ - Return a new Catalog with a subset of the entries in this Catalog. - - Parameters - ---------- - query : dict - MongoDB query. - """ - query = dict(query) - if query: - query = {f"start.{key}": val for key, val in query.items()} - if self._query: - query = {'$and': [self._query, query]} - cat = type(self)( - datastore_db=self._db, - query=query, - handler_registry=self._handler_registry, - transforms=self._transforms, - root_map=self._root_map, - filler_class=self._filler_class, - name='search results', - getenv=self.getenv, - getshell=self.getshell, - auth=self.auth, - metadata=(self.metadata or {}).copy(), - storage_options=self.storage_options) - return cat
- - -def _get_database(uri): - client = pymongo.MongoClient(uri) - try: - # Called with no args, get_database() returns the database - # specified in the client's uri --- or raises if there was none. - # There is no public method for checking this in advance, so we - # just catch the error. - return client.get_database() - except pymongo.errors.ConfigurationError as err: - raise ValueError( - f"Invalid client: {client} " - f"Did you forget to include a database?") from err -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/_drivers/mongo_normalized.html b/databroker/_modules/databroker/_drivers/mongo_normalized.html deleted file mode 100644 index ad2b31cacd..0000000000 --- a/databroker/_modules/databroker/_drivers/mongo_normalized.html +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - - - - databroker._drivers.mongo_normalized — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker._drivers.mongo_normalized
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker._drivers.mongo_normalized

-import collections.abc
-import event_model
-from functools import partial
-import logging
-import cachetools
-import pymongo
-import pymongo.errors
-
-from bson.objectid import ObjectId, InvalidId
-from ..core import to_event_pages, to_datum_pages, Entry
-from ..v2 import Broker
-
-
-logger = logging.getLogger(__name__)
-
-
-class _Entries(collections.abc.Mapping):
-    "Mock the dict interface around a MongoDB query result."
-    def __init__(self, catalog):
-        self.catalog = catalog
-        self.__cache = cachetools.LRUCache(1024)
-
-    def cache_clear(self):
-        self.__cache.clear()
-
-    def _doc_to_entry(self, run_start_doc):
-        uid = run_start_doc['uid']
-        run_start_doc.pop('_id')
-        entry_metadata = {'start': run_start_doc,
-                          'stop': self.catalog._get_run_stop(uid)}
-
-        def get_run_start():
-            return run_start_doc
-
-        args = dict(
-            get_run_start=get_run_start,
-            get_run_stop=partial(self.catalog._get_run_stop, uid),
-            get_event_descriptors=partial(self.catalog._get_event_descriptors, uid),
-            # 2500 was selected as the page_size because it worked well durring
-            # benchmarks, for HXN data a full page had roughly 3500 events.
-            get_event_pages=to_event_pages(self.catalog._get_event_cursor, 2500),
-            get_event_count=self.catalog._get_event_count,
-            get_resource=self.catalog._get_resource,
-            get_resources=partial(self.catalog._get_resources, uid),
-            lookup_resource_for_datum=self.catalog._lookup_resource_for_datum,
-            # 2500 was selected as the page_size because it worked well durring
-            # benchmarks.
-            get_datum_pages=to_datum_pages(self.catalog._get_datum_cursor, 2500),
-            get_filler=self.catalog._get_filler,
-            transforms=self.catalog._transforms)
-        return Entry(
-            name=run_start_doc['uid'],
-            description={},  # TODO
-            driver='databroker.core.BlueskyRun',
-            direct_access='forbid',  # ???
-            args=args,
-            cache=None,  # ???
-            parameters=[],
-            metadata=entry_metadata,
-            catalog_dir=None,
-            getenv=True,
-            getshell=True,
-            catalog=self.catalog)
-
-    def __iter__(self):
-        find_kwargs = {'sort': [('time', pymongo.DESCENDING)]}
-        find_kwargs.update(self.catalog._find_kwargs)
-        cursor = self.catalog._run_start_collection.find(
-            self.catalog._query, **find_kwargs)
-        for run_start_doc in cursor:
-            yield run_start_doc['uid']
-
-    def __getitem__(self, name):
-        run_start_doc = self._find_run_start_doc(name)
-        uid = run_start_doc['uid']
-        try:
-            entry = self.__cache[uid]
-            logger.debug('Mongo Entries cache found %r', uid)
-        except KeyError:
-            entry = self._doc_to_entry(run_start_doc)
-            self.__cache[uid] = entry
-        # The user has requested one specific Entry. In order to give them a
-        # more useful object, 'get' the Entry for them. Note that if they are
-        # expecting an Entry and try to call ``()`` or ``.get()``, that will
-        # still work because BlueskyRun supports those methods and will just
-        # return itself.
-        return entry.get()  # an instance of BlueskyRun
-
-    def _find_run_start_doc(self, name):
-        # If this came from a client, we might be getting '-1'.
-        collection = self.catalog._run_start_collection
-        try:
-            N = int(name)
-        except ValueError:
-            query = {'$and': [self.catalog._query, {'uid': name}]}
-            run_start_doc = collection.find_one(query)
-            if run_start_doc is None:
-                regex_query = {
-                    '$and': [self.catalog._query,
-                             {'uid': {'$regex': f'^{name}'}}]}
-                matches = list(collection.find(regex_query).limit(10))
-                if not matches:
-                    raise KeyError(name)
-                elif len(matches) == 1:
-                    run_start_doc, = matches
-                else:
-                    match_list = '\n'.join(doc['uid'] for doc in matches)
-                    raise ValueError(
-                        f"Multiple matches to partial uid {name!r}. "
-                        f"Up to 10 listed here:\n"
-                        f"{match_list}")
-        else:
-            if N < 0:
-                # Interpret negative N as "the Nth from last entry".
-                query = self.catalog._query
-                cursor = (collection.find(query)
-                          .sort('time', pymongo.DESCENDING)
-                          .skip(-N - 1)
-                          .limit(1))
-                try:
-                    run_start_doc, = cursor
-                except ValueError:
-                    raise IndexError(
-                        f"Catalog only contains {len(self.catalog)} "
-                        f"runs.")
-            else:
-                # Interpret positive N as
-                # "most recent entry with scan_id == N".
-                query = {'$and': [self.catalog._query, {'scan_id': N}]}
-                cursor = (collection.find(query)
-                          .sort('time', pymongo.DESCENDING)
-                          .limit(1))
-                try:
-                    run_start_doc, = cursor
-                except ValueError:
-                    raise KeyError(f"No run with scan_id={N}")
-        if run_start_doc is None:
-            raise KeyError(name)
-        return run_start_doc
-
-    def __contains__(self, key):
-        # Try the fast path first.
-        if key in self.__cache:
-            return True
-        # Avoid paying for creating the Entry yet. Do just enough work decide
-        # if we *can* create such an Entry.
-        try:
-            self._find_run_start_doc(key)
-        except KeyError:
-            return False
-        else:
-            return True
-
-    def __len__(self):
-        return len(self.catalog)
-
-
-
[docs]class BlueskyMongoCatalog(Broker): - def __init__(self, metadatastore_db, asset_registry_db, *, - handler_registry=None, root_map=None, - filler_class=event_model.Filler, query=None, - find_kwargs=None, transforms=None, **kwargs): - """ - This Catalog is backed by a pair of MongoDBs with "layout 1". - - This layout uses a separate Mongo collection per document type and a - separate Mongo document for each logical document. - - Parameters - ---------- - metadatastore_db : pymongo.database.Database or string - Must be a Database or a URI string that includes a database name. - asset_registry_db : pymongo.database.Database or string - Must be a Database or a URI string that includes a database name. - handler_registry : dict, optional - This is passed to the Filler or whatever class is given in the - ``filler_class`` parameter below. - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - A 'handler class' may be any callable with the signature:: - - handler_class(resource_path, root, **resource_kwargs) - - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - - handler_instance(**datum_kwargs) - - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the - ``filler_class`` parameter below. - str -> str mapping to account for temporarily - moved/copied/remounted files. Any resources which have a ``root`` - in ``root_map`` will be loaded using the mapped ``root``. - filler_class: type, optional - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the - same methods as ``DocumentRouter``. - query : dict, optional - MongoDB query. Used internally by the ``search()`` method. - find_kwargs : dict, optional - Options passed to pymongo ``find``. - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - name = 'bluesky-mongo-catalog' # noqa - - if isinstance(metadatastore_db, str): - mds_db = _get_database(metadatastore_db) - else: - mds_db = metadatastore_db - if isinstance(asset_registry_db, str): - assets_db = _get_database(asset_registry_db) - else: - assets_db = asset_registry_db - - self._run_start_collection = mds_db.get_collection('run_start') - self._run_stop_collection = mds_db.get_collection('run_stop') - self._event_descriptor_collection = mds_db.get_collection('event_descriptor') - self._event_collection = mds_db.get_collection('event') - self._resource_collection = assets_db.get_collection('resource') - self._datum_collection = assets_db.get_collection('datum') - self._metadatastore_db = mds_db - self._asset_registry_db = assets_db - self._query = query or {} - self._find_kwargs = find_kwargs or {} - - super().__init__(handler_registry=handler_registry, - root_map=root_map, filler_class=filler_class, - transforms=transforms, **kwargs) - - def _get_run_stop(self, run_start_uid): - doc = self._run_stop_collection.find_one( - {'run_start': run_start_uid}, - {'_id': False}) - return doc - - def _get_event_descriptors(self, run_start_uid): - cursor = self._event_descriptor_collection.find( - {'run_start': run_start_uid}, - {'_id': False}, - sort=[('time', pymongo.ASCENDING)]) - return list(cursor) - - def _get_event_cursor(self, descriptor_uid, skip=0, limit=None): - cursor = (self._event_collection - .find({'descriptor': descriptor_uid}, - {'_id': False}, - sort=[('time', pymongo.ASCENDING)])) - descriptor = self._event_descriptor_collection.find_one( - {'uid': descriptor_uid}) - cursor.skip(skip) - if limit is not None: - cursor = cursor.limit(limit) - external_keys = {k for k, v in descriptor['data_keys'].items() - if 'external' in v} - for doc in cursor: - doc['filled'] = {k: False for k in external_keys} - yield doc - - def _get_event_count(self, descriptor_uid): - return self._event_collection.count_documents( - {'descriptor': descriptor_uid}) - - def _get_resources(self, run_start_uid): - return list(self._resource_collection.find( - {'run_start': run_start_uid}, {'_id': False})) - - def _get_resource(self, uid): - doc = self._resource_collection.find_one({'uid': uid}, {'_id': False}) - - # Some old resource documents don't have a 'uid' and they are - # referenced by '_id'. - if doc is None: - try: - _id = ObjectId(uid) - except InvalidId: - pass - else: - doc = self._resource_collection.find_one({'_id': _id}, {'_id': False}) - doc['uid'] = uid - - if doc is None: - raise ValueError(f"Could not find Resource with uid={uid}") - return doc - - def _lookup_resource_for_datum(self, datum_id): - doc = self._datum_collection.find_one( - {'datum_id': datum_id}) - if doc is None: - raise ValueError(f"Could not find Datum with datum_id={datum_id}") - return doc['resource'] - - def _get_datum_cursor(self, resource_uid): - self._schema = {} # TODO This is cheating, I think. - return self._datum_collection.find({'resource': resource_uid}, {'_id': False}) - - def _make_entries_container(self): - return _Entries(self) - - def __len__(self): - return self._run_start_collection.count_documents(self._query) - - def _close(self): - self._client.close() - -
[docs] def search(self, query, **kwargs): - """ - Return a new Catalog with a subset of the entries in this Catalog. - - Parameters - ---------- - query : dict - MongoDB query. - **kwargs : - Options passed through to the pymongo ``find()`` method - """ - query = dict(query) - if self._query: - query = {'$and': [self._query, query]} - cat = type(self)( - metadatastore_db=self._metadatastore_db, - asset_registry_db=self._asset_registry_db, - query=query, - find_kwargs=kwargs, - handler_registry=self._handler_registry, - transforms=self._transforms, - root_map=self._root_map, - filler_class=self._filler_class, - name='search results', - getenv=self.getenv, - getshell=self.getshell, - auth=self.auth, - metadata=(self.metadata or {}).copy(), - storage_options=self.storage_options) - return cat
- - def _get_serializer(self): - "This is used internally by v1.Broker. It may be removed in future." - from suitcase.mongo_normalized import Serializer - return Serializer(self._metadatastore_db, self._asset_registry_db) - -
[docs] def stats(self): - "Access MongoDB storage statistics for this database." - return self._run_start_collection.database.command("dbstats")
- - -def _get_database(uri): - client = pymongo.MongoClient(uri) - try: - # Called with no args, get_database() returns the database - # specified in the client's uri --- or raises if there was none. - # There is no public method for checking this in advance, so we - # just catch the error. - return client.get_database() - except pymongo.errors.ConfigurationError as err: - raise ValueError( - f"Invalid client: {client} " - f"Did you forget to include a database?") from err -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/_drivers/msgpack.html b/databroker/_modules/databroker/_drivers/msgpack.html deleted file mode 100644 index 39cc08fec0..0000000000 --- a/databroker/_modules/databroker/_drivers/msgpack.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - - - - - - databroker._drivers.msgpack — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker._drivers.msgpack
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker._drivers.msgpack

-import event_model
-import glob
-import msgpack
-import msgpack_numpy
-import os
-import pathlib
-
-from ..in_memory import BlueskyInMemoryCatalog
-
-
-UNPACK_OPTIONS = dict(object_hook=msgpack_numpy.decode,
-                      raw=False,
-                      max_buffer_size=1_000_000_000)
-
-
-def gen(filename):
-    """
-    A msgpack generator
-
-    Parameters
-    ----------
-    filename: str
-        msgpack file to laod.
-    """
-    with open(filename, 'rb') as file:
-        yield from msgpack.Unpacker(file, **UNPACK_OPTIONS)
-
-
-def get_stop(filename):
-    """
-    Returns the stop_doc of a Bluesky msgpack file.
-
-    The stop_doc is always the last line of the file.
-
-    Parameters
-    ----------
-    filename: str
-        msgpack file to load.
-    Returns
-    -------
-    stop_doc: dict or None
-        A Bluesky run_stop document or None if one is not present.
-    """
-    with open(filename, 'rb') as file:
-        for name, doc in msgpack.Unpacker(file, **UNPACK_OPTIONS):
-            if name == 'stop':
-                return doc
-
-
-
[docs]class BlueskyMsgpackCatalog(BlueskyInMemoryCatalog): - name = 'bluesky-msgpack-catalog' # noqa - - def __init__(self, paths, *, - handler_registry=None, root_map=None, - filler_class=event_model.Filler, query=None, - transforms=None, **kwargs): - """ - This Catalog is backed by msgpack files. - - Each chunk the file is expected to be a list with two elements, - the document name (type) and the document itself. The documents are - expected to be in chronological order. - - Parameters - ---------- - paths : list - list of filepaths - handler_registry : dict, optional - This is passed to the Filler or whatever class is given in the - ``filler_class`` parameter below. - - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - - A 'handler class' may be any callable with the signature:: - - handler_class(resource_path, root, **resource_kwargs) - - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - - handler_instance(**datum_kwargs) - - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the - ``filler_class`` parameter below. - str -> str mapping to account for temporarily moved/copied/remounted - files. Any resources which have a ``root`` in ``root_map`` will be - loaded using the mapped ``root``. - filler_class: type, optional - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the - same methods as ``DocumentRouter``. - query : dict, optional - Mongo query that filters entries' RunStart documents - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - # Tolerate a single path (as opposed to a list). - if isinstance(paths, (str, pathlib.Path)): - paths = [paths] - self.paths = paths - self._filename_to_mtime = {} - super().__init__(handler_registry=handler_registry, - root_map=root_map, filler_class=filler_class, - query=query, transforms=transforms, **kwargs) - - def _load(self): - for path in self.paths: - for filename in glob.glob(path): - mtime = os.path.getmtime(filename) - if mtime == self._filename_to_mtime.get(filename): - # This file has not changed since last time we loaded it. - continue - self._filename_to_mtime[filename] = mtime - with open(filename, 'rb') as file: - unpacker = msgpack.Unpacker(file, **UNPACK_OPTIONS) - try: - name, start_doc = next(unpacker) - except StopIteration: - # Empty file, maybe being written to currently - continue - stop_doc = get_stop(filename) - self.upsert(start_doc, stop_doc, gen, (filename,), {}) - -
[docs] def search(self, query): - """ - Return a new Catalog with a subset of the entries in this Catalog. - - Parameters - ---------- - query : dict - """ - query = dict(query) - if self._query: - query = {'$and': [self._query, query]} - cat = type(self)( - paths=self.paths, - query=query, - handler_registry=self._handler_registry, - transforms=self._transforms, - root_map=self._root_map, - name='search results', - getenv=self.getenv, - getshell=self.getshell, - auth=self.auth, - metadata=(self.metadata or {}).copy(), - storage_options=self.storage_options) - return cat
- - def _get_serializer(self): - "This is used internally by v1.Broker. It may be removed in future." - from suitcase.msgpack import Serializer - from event_model import RunRouter - path, *_ = self.paths - directory = os.path.dirname(path) - - def factory(name, doc): - serializer = Serializer(directory) - return [serializer], [] - - return RunRouter([factory])
-
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/broker.html b/databroker/_modules/databroker/broker.html deleted file mode 100644 index 29088b9741..0000000000 --- a/databroker/_modules/databroker/broker.html +++ /dev/null @@ -1,1622 +0,0 @@ - - - - - - - - - - - - databroker.broker — databroker 0.8.4+265.g5848a59 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -

Source code for databroker.broker

-from __future__ import print_function
-from importlib import import_module
-import itertools
-import warnings
-import six  # noqa
-import logging
-import numbers
-import doct
-import pandas as pd
-import sys
-import os
-import yaml
-import glob
-import tempfile
-import copy
-from collections import defaultdict
-
-
-from .core import (Header,
-                   get_fields,  # for convenience
-                   Images,
-                   ALL, format_time,
-                   register_builtin_handlers)
-from .eventsource import EventSourceShim, check_fields_exist
-from .headersource import HeaderSourceShim, safe_get_stop
-
-
-try:
-    from functools import singledispatch
-except ImportError:
-    try:
-        # We are running on Python 2.6, 2.7, or 3.3
-        from singledispatch import singledispatch
-    except ImportError:
-        raise ImportError(
-            "Please install singledispatch from PyPI"
-            "\n\n   pip install singledispatch"
-            "\n\nThen run your program again."
-        )
-try:
-    from collections.abc import MutableSequence
-except ImportError:
-    # This will error on python < 3.3
-    from collections import MutableSequence
-
-
-logger = logging.getLogger(__name__)
-
-
-@singledispatch
-def search(key, db):
-    logger.info('Using default search for key = %s' % key)
-    raise ValueError("Must give an integer scan ID like [6], a slice "
-                     "into past scans like [-5], [-5:], or [-5:-9:2], "
-                     "a list like [1, 7, 13], a (partial) uid "
-                     "like ['a23jslk'] or a full uid like "
-                     "['f26efc1d-8263-46c8-a560-7bf73d2786e1'].")
-
-
-@search.register(slice)
-def _(key, db):
-    # Interpret key as a slice into previous scans.
-    logger.info('Interpreting key = %s as a slice' % key)
-    if key.start is not None and key.start > -1:
-        raise ValueError("slice.start must be negative. You gave me "
-                         "key=%s The offending part is key.start=%s"
-                         % (key, key.start))
-    if key.stop is not None and key.stop > 0:
-        raise ValueError("slice.stop must be <= 0. You gave me key=%s. "
-                         "The offending part is key.stop = %s"
-                         % (key, key.stop))
-    if key.stop is not None:
-        stop = -key.stop
-    else:
-        stop = None
-    if key.start is None:
-        raise ValueError("slice.start cannot be None because we do not "
-                         "support slicing infinitely into the past; "
-                         "the size of the result is non-deterministic "
-                         "and could become too large.")
-    start = -key.start
-    result = list(db.hs.find_last(start))[stop::key.step]
-    stop = list(safe_get_stop(db.hs, s) for s in result)
-    return list(zip(result, stop))
-
-
-@search.register(numbers.Integral)
-def _(key, db):
-    logger.info('Interpreting key = %s as an integer' % key)
-    if key > -1:
-        # Interpret key as a scan_id.
-        gen = db.hs.find_run_starts(scan_id=key)
-        try:
-            result = next(gen)  # most recent match
-        except StopIteration:
-            raise ValueError("No such run found for key=%s which is "
-                             "being interpreted as a scan id." % key)
-    else:
-        # Interpret key as the Nth last scan.
-        gen = db.hs.find_last(-key)
-        for i in range(-key):
-            try:
-                result = next(gen)
-            except StopIteration:
-                raise IndexError(
-                    "There are only {0} runs.".format(i))
-    return [(result, safe_get_stop(db.hs, result))]
-
-
-@search.register(str)
-@search.register(six.text_type)
-@search.register(six.string_types,)
-def _(key, db):
-    logger.info('Interpreting key = %s as a str' % key)
-    results = None
-    if len(key) == 36:
-        # Interpret key as a complete uid.
-        # (Try this first, for performance.)
-        logger.debug('Treating %s as a full uuid' % key)
-        results = list(db.hs.find_run_starts(uid=key))
-        logger.debug('%s runs found for key=%s treated as a full uuid'
-                     % (len(results), key))
-    if not results:
-        # No dice? Try searching as if we have a partial uid.
-        logger.debug('Treating %s as a partial uuid' % key)
-        gen = db.hs.find_run_starts(uid={'$regex': '{0}.*'.format(key)})
-        results = list(gen)
-    if not results:
-        # Still no dice? Bail out.
-        raise ValueError("No such run found for key=%r" % key)
-    if len(results) > 1:
-        raise ValueError("key=%r matches %d runs. Provide "
-                         "more characters." % (key, len(results)))
-    result, = results
-    return [(result, safe_get_stop(db.hs, result))]
-
-
-@search.register(set)
-@search.register(tuple)
-@search.register(MutableSequence)
-def _(key, db):
-    logger.info('Interpreting key = {} as a set, tuple or MutableSequence'
-                ''.format(key))
-    return sum((search(k, db) for k in key), [])
-
-
-class Results(object):
-    """
-    Iterable object encapsulating a results set of Headers
-
-    Parameters
-    ----------
-    res : iterable
-        Iterable of ``(start_doc, stop_doc)`` pairs
-    db : :class:`Broker`
-    data_key : string or None
-        Special query parameter that filters results
-    """
-    def __init__(self, res, db, data_key):
-        self._db = db
-        self._res = res
-        self._data_key = data_key
-
-    def __iter__(self):
-        self._res, res = itertools.tee(self._res)
-        for start, stop in res:
-            header = Header(start=self._db.prepare_hook('start', start),
-                            stop=self._db.prepare_hook('stop', stop),
-                            db=self._db)
-            if self._data_key is None:
-                yield header
-            else:
-                # Only include this header in the result if `data_key` is found
-                # in one of its descriptors' data_keys.
-                for descriptor in header['descriptors']:
-                    if self._data_key in descriptor['data_keys']:
-                        yield header
-                        break
-
-# Search order is:
-# ~/.config/databroker
-# <sys.executable directory>/../etc/databroker
-# /etc/databroker
-
-
-_user_conf = os.path.join(os.path.expanduser('~'), '.config', 'databroker')
-_local_etc = os.path.join(os.path.dirname(os.path.dirname(sys.executable)),
-                          'etc', 'databroker')
-_system_etc = os.path.join('etc', 'databroker')
-CONFIG_SEARCH_PATH = (_user_conf, _local_etc, _system_etc)
-
-
-if six.PY2:
-    FileNotFoundError = IOError
-
-
-
[docs]def list_configs(): - """ - List the names of the available configuration files. - - Returns - ------- - names : list - """ - names = [] - for path in CONFIG_SEARCH_PATH: - files = glob.glob(os.path.join(path, '*.yml')) - names.extend([os.path.basename(f)[:-4] for f in files]) - return sorted(names)
- - -
[docs]def lookup_config(name): - """ - Search for a databroker configuration file with a given name. - - For exmaple, the name 'example' will cause the function to search for: - - * ``~/.config/databroker/example.yml`` - * ``{python}/../etc/databroker/example.yml`` - * ``/etc/databroker/example.yml`` - - where ``{python}`` is the location of the current Python binary, as - reported by ``sys.executable``. It will use the first match it finds. - - Parameters - ---------- - name : string - - Returns - ------- - config : dict - """ - if not name.endswith('.yml'): - name += '.yml' - tried = [] - for path in CONFIG_SEARCH_PATH: - filename = os.path.join(path, name) - tried.append(filename) - if os.path.isfile(filename): - with open(filename) as f: - return yaml.load(f) - else: - raise FileNotFoundError("No config file named {!r} could be found in " - "the following locations:\n{}" - "".format(name, '\n'.join(tried)))
- - -def load_component(config): - modname = config['module'] - clsname = config['class'] - config = config['config'] - mod = import_module(modname) - cls = getattr(mod, clsname) - return cls(config) - - -
[docs]def temp_config(): - """ - Generate Broker configuration backed by temporary, disposable databases. - - This is suitable for testing and experimentation, but it is not recommended - for large or important data. - - Returns - ------- - config : dict - - Examples - -------- - This is the fastest way to get up and running with a Broker. - - >>> c = temp_config() - >>> db = Broker.from_config(c) - """ - tempdir = tempfile.mkdtemp() - config = { - 'metadatastore': { - 'module': 'databroker.headersource.sqlite', - 'class': 'MDS', - 'config': { - 'directory': tempdir, - 'timezone': 'US/Eastern'} - }, - 'assets': { - 'module': 'databroker.assets.sqlite', - 'class': 'Registry', - 'config': { - 'dbpath': os.path.join(tempdir, 'assets.sqlite')} - } - } - return config
- - -DOCT_NAMES = {'resource': 'Resource', - 'datum': 'Datum', - 'descriptor': 'Event Descriptor', - 'event': 'Event', - 'start': 'Run Start', - 'stop': 'Run Stop'} - - -
[docs]def wrap_in_doct(name, doc): - """ - Put document contents into a doct.Document object. - - A ``doct.Document`` is a subclass of dict that: - - * is immutable - * provides human-readable :meth:`__repr__` and :meth:`__str__` - * supports dot access (:meth:`__getattr__`) as a synonym for item access - (:meth:`__getitem__`) whenever possible - """ - return doct.Document(DOCT_NAMES[name], doc)
- - -_STANDARD_DICT_ATTRS = dir(dict) - - -class DeprecatedDoct(doct.Document): - "Subclass of doct.Document that warns that dot access may be removed." - def __getattribute__(self, key): - # Get the result first and let any errors be raised. - res = super(DeprecatedDoct, self).__getattribute__(key) - # Now warn before returning it. - if key not in _STANDARD_DICT_ATTRS: - # This is not a standard dict attribute. - # Warn that dot access is deprecated. - warnings.warn("Dot access may be removed in a future version." - "Use [{0}] instead of .{0}".format(key)) - return res - - -
[docs]def wrap_in_deprecated_doct(name, doc): - """ - Put document contents into a DeprecatedDoct object. - - See :func:`wrap_in_doct`. The difference between :class:`DeprecatedDoct` - and :class:`doct.Document` is a warning that dot access - (:meth:`__getattr__` as a synonym for :meth:`__getitem__`) may be removed - in the future. - """ - return DeprecatedDoct(DOCT_NAMES[name], doc)
- - -class BrokerES(object): - """ - Unified interface to data sources - - Parameters - ---------- - hs : HeaderSource - *event_sources : - zero, one or more EventSource objects - """ - def __init__(self, hs, event_sources, assets): - self.hs = hs - self.event_sources = event_sources - self.assets = assets - # Once we drop Python 2, we can accept initial filter and aliases as - # keyword-only args if we want to. - self.filters = [] - self.aliases = {} - self.event_source_for_insert = self.event_sources[0] - self.registry_for_insert = self.event_sources[0] - self.prepare_hook = wrap_in_deprecated_doct - - def add_event_source(self, es): - self.event_sources.append(es) - - def stream_names_given_header(self, header): - return [n for es in self.event_sources - for n in es.stream_names_given_header(header)] - - def insert(self, name, doc): - """ - Insert a new document. - - Parameters - ---------- - name : {'start', 'descriptor', 'event', 'stop'} - Document type - doc : dict - Document - """ - if name in {'start', 'stop'}: - return self.hs.insert(name, doc) - else: - return self.event_source_for_insert.insert(name, doc) - - @property - def mds(self): - return self.hs.mds - - @property - def reg(self): - return self.assets[''] - - @property - def fs(self): - warnings.warn("fs is deprecated, use `db.reg` instead", - stacklevel=2) - return self.reg - - ALL = ALL # sentinel used as default value for `stream_name` - - def _format_time(self, val): - "close over the timezone config" - # modifies a query dict in place, remove keys 'start_time' and - # 'stop_time' and adding $lte and/or $gte queries on 'time' key - format_time(val, self.hs.mds.config['timezone']) - - @property - def filters(self): - return self._filters - - @filters.setter - def filters(self, val): - for elem in val: - self._format_time(elem) - self._filters = val - - def add_filter(self, **kwargs): - """ - Add query to the list of 'filter' queries. - - Filter queries are combined with every given query using '$and', - acting as a filter to restrict the results. - - ``db.add_filter(**kwargs)`` is just a convenient way to spell - ``db.filters.append(dict(**kwargs))``. - - Examples - -------- - Filter all searches to restrict runs to a specific 'user'. - - >>> db.add_filter(user='Dan') - - See Also - -------- - :meth:`Broker.clear_filters` - - """ - self.filters.append(dict(**kwargs)) - - def clear_filters(self, **kwargs): - """ - Clear all 'filter' queries. - - Filter queries are combined with every given query using '$and', - acting as a filter to restrict the results. - - ``Broker.clear_filters()`` is just a convenient way to spell - ``Broker.filters.clear()``. - - See Also - -------- - :meth:`Broker.add_filter` - """ - self.filters.clear() - - def __getitem__(self, key): - """ - Search runs based on recently, unique id, or counting number scan_id. - - This function returns a :class:`Header` object (or a list of them, if - the input is a list or slice). Each Header encapsulates the metadata - for a run -- start time, instruments used, and so on, and provides - methods for loading the data. - - Examples - -------- - Get the most recent run. - - >>> header = db[-1] - - Get the fifth most recent run. - - >>> header = db[-5] - - Get a list of all five most recent runs, using Python slicing syntax. - - >>> headers = db[-5:] - - Get a run whose unique ID ("RunStart uid") begins with 'x39do5'. - - >>> header = db['x39do5'] - - Get a run whose integer scan_id is 42. Note that this might not be - unique. In the event of duplicates, the most recent match is returned. - - >>> header = db[42] - """ - ret = [Header(start=self.prepare_hook('start', start), - stop=self.prepare_hook('stop', stop), - db=self) - for start, stop in search(key, self)] - squeeze = not isinstance(key, (set, tuple, MutableSequence, slice)) - if squeeze and len(ret) == 1: - ret, = ret - return ret - - def __getattr__(self, key): - try: - query = self.aliases[key] - except KeyError: - raise AttributeError(key) - if callable(query): - query = query() - return self(**query) - - def alias(self, key, **query): - """ - Create an alias for a query. - - Parameters - ---------- - key : string - must be a valid Python identifier - query : - keyword argument comprising a query - - Examples - -------- - >>> db.alias('cal', purpose='calibration') - """ - if hasattr(self, key) and key not in self.aliases: - raise ValueError("'%s' is not a legal alias." % key) - self.aliases[key] = query - - def dynamic_alias(self, key, func): - """ - Create an alias for a "dynamic" query, a function that returns a query. - - Parameters - ---------- - key : string - must be a valid Python identifier - func : callable - When called with no arguments, must return a dict that is a valid - query. - - Examples - -------- - Get headers from the last 24 hours. - >>> import time - >>> db.dynamic_alias('today', - lambda: {'start_time': - start_time=time.time()- 24*60*60}) - """ - if hasattr(self, key) and key not in self.aliases: - raise ValueError("'%s' is not a legal alias." % key) - self.aliases[key] = func - - def __call__(self, text_search=None, **kwargs): - """Search runs based on metadata. - - This function returns an iterable of :class:`Header` objects. Each - Header encapsulates the metadata for a run -- start time, instruments - used, and so on, and provides methods for loading the data. In - addition to the Parameters below, advanced users can specifiy arbitrary - queries using the MongoDB query syntax. - - The ``start_time`` and ``stop_time`` parameters accepts the following - representations of time: - - * timestamps like ``time.time()`` and ``datetime.datetime.now()`` - * ``'2015'`` - * ``'2015-01'`` - * ``'2015-01-30'`` - * ``'2015-03-30 03:00:00'`` - - Parameters - ---------- - text_search : str, optional - search full text of RunStart documents - start_time : str, optional - Restrict results to runs that started after this time. - stop_time : str, optional - Restrict results to runs that started before this time. - data_key : str, optional - Restrict results to runs that contained this data_key (a.k.a field) - such as 'intensity' or 'temperature'. - **kwargs - query parameters - - Returns - ------- - data : :class:`Results` - Iterable object encapsulating a results set of Headers - - Examples - -------- - Search by plan name. - - >>> db(plan_name='scan') - - Search for runs involving a motor with the name 'eta'. - - >>> db(motor='eta') - - Search for runs operated by a given user---assuming this metadata was - recorded in the first place! - - >>> db(operator='Dan') - - Search by time range. (These keywords have a special meaning.) - - >>> db(start_time='2015-03-05', stop_time='2015-03-10') - - Perform text search on all values in the Run Start document. - - >>> db('keyword') - - Note that partial words are not matched, but partial phrases are. For - example, 'good' will match to 'good sample' but 'goo' will not. - """ - data_key = kwargs.pop('data_key', None) - - res = self.hs(text_search=text_search, - filters=self.filters, - **kwargs) - - return Results(res, self, data_key) - - def fill_event(self, event, inplace=True, handler_registry=None): - """ - Deprecated, use `fill_events` instead. - - Populate events with externally stored data. - - Parameters - ---------- - event : document - inplace : bool, optional - If the event should be filled 'in-place' by mutating the data - dictionary. Defaults to `True`. - handler_registry : dict, optional - mapping spec names (strings) to handlers (callable classes) - """ - warnings.warn("fill_event is deprecated, use fill_events instead", - stacklevel=2) - # TODO sort out how to (quickly) map events back to the - # correct event Source - desc_id = event['descriptor'] - descs = [] - for es in self.event_sources: - try: - d = es.descriptor_given_uid(desc_id) - except es.NoEventDescriptors: - pass - else: - descs.append(d) - - # dirty hack! - with self.reg.handler_context(handler_registry): - ev_out, = self.fill_events([event], descs, inplace=inplace) - return ev_out - - def get_events(self, - headers, stream_name='primary', fields=None, fill=False, - handler_registry=None): - """ - Get Event documents from one or more runs. - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is 'primary' - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping asset specs (strings) to handlers (callable classes) - - Yields - ------ - event : Event - The event, optionally with non-scalar data filled in - - Raises - ------ - ValueError if any key in `fields` is not in at least one descriptor - pre header. - """ - for name, doc in self.get_documents(headers, - fields=fields, - stream_name=stream_name, - fill=fill, - handler_registry=handler_registry): - if name == 'event': - yield doc - - def get_documents(self, - headers, stream_name=ALL, fields=None, fill=False, - handler_registry=None): - """ - Get all documents from one or more runs. - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is `ALL` which yields documents for all streams. - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping asset pecs (strings) to handlers (callable classes) - - Yields - ------ - name : str - The name of the kind of document - - doc : dict - The payload, may be RunStart, RunStop, EventDescriptor, or Event. - - Raises - ------ - ValueError if any key in `fields` is not in at least one descriptor - pre header. - """ - try: - headers.items() - except AttributeError: - pass - else: - headers = [headers] - - check_fields_exist(fields if fields else [], headers) - # dirty hack! - with self.reg.handler_context(handler_registry): - for h in headers: - if not isinstance(h, Header): - h = self[h['start']['uid']] - # TODO filter fill by fields - proc_gen = self._fill_events_coro(h.descriptors, - fields=fill, - inplace=True) - proc_gen.send(None) - for es in self.event_sources: - gen = es.docs_given_header( - header=h, stream_name=stream_name, - fields=fields) - for name, doc in gen: - if name == 'event': - doc = proc_gen.send(doc) - yield name, self.prepare_hook(name, doc) - proc_gen.close() - - def get_table(self, - headers, stream_name='primary', fields=None, fill=False, - handler_registry=None, - convert_times=True, timezone=None, localize_times=True): - """ - Load the data from one or more runs as a table (``pandas.DataFrame``). - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is 'primary' - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping filestore specs (strings) to handlers (callable classes) - - convert_times : bool, optional - Whether to convert times from float (seconds since 1970) to - numpy datetime64, using pandas. True by default. - - timezone : str, optional - e.g., 'US/Eastern'; if None, use metadatastore configuration in - `self.mds.config['timezone']` - - handler_registry : dict, optional - mapping asset specs (strings) to handlers (callable classes) - - localize_times : bool, optional - If the times should be localized to the 'local' time zone. If - True (the default) the time stamps are converted to the localtime - zone (as configure in mds). - - This is problematic for several reasons: - - - apparent gaps or duplicate times around DST transitions - - incompatibility with every other time stamp (which is in UTC) - - however, this makes the dataframe repr look nicer - - This implies convert_times. - - Defaults to True to preserve back-compatibility. - - Returns - ------- - table : pandas.DataFrame - """ - try: - headers.items() - except AttributeError: - pass - else: - headers = [headers] - - dfs = [] - # dirty hack! - with self.reg.handler_context(handler_registry): - for h in headers: - if not isinstance(h, Header): - h = self[h['start']['uid']] - - # get the first descriptor for this event stream - desc = next((d for d in h.descriptors - if d.name == stream_name), - None) - if desc is None: - continue - for es in self.event_sources: - table = es.table_given_header( - header=h, - fields=fields, - stream_name=stream_name, - convert_times=convert_times, - timezone=timezone, - localize_times=localize_times) - if len(table): - table = self.fill_table(table, desc, inplace=True) - dfs.append(table) - - if dfs: - df = pd.concat(dfs) - else: - # edge case: no data - df = pd.DataFrame() - df.index.name = 'seq_num' - - return df - - def get_images(self, headers, name, - stream_name='primary', - handler_registry=None,): - """ - This method is deprecated. Use Broker.get_documents instead. - - Load image data from one or more runs into a lazy array-like object. - - Parameters - ---------- - headers : Header or list of Headers - name : string - field name (data key) of a detector - handler_registry : dict, optional - mapping spec names (strings) to handlers (callable classes) - - Examples - -------- - >>> header = DataBroker[-1] - >>> images = Images(header, 'my_detector_lightfield') - >>> for image in images: - # do something - """ - - # TODO sort out how to broadcast this - return Images(mds=self.mds, reg=self.reg, es=self.event_sources[0], - headers=headers, - name=name, stream_name=stream_name, - handler_registry=handler_registry) - - def get_resource_uids(self, header): - '''Given a Header, give back a list of resource uids - - These uids are required to move the underlying files. - - Parameters - ---------- - header : Header - - Returns - ------- - ret : set - set of resource uids which are refereneced by this Header. - ''' - external_keys = set() - for d in header['descriptors']: - for k, v in six.iteritems(d['data_keys']): - if 'external' in v: - external_keys.add(k) - ev_gen = self.get_events(header, stream_name=ALL, - fields=external_keys, fill=False) - resources = set() - for ev in ev_gen: - for k, v in six.iteritems(ev['data']): - if k in external_keys: - res = self.reg.resource_given_datum_id(v) - resources.add(res['uid']) - return resources - - def restream(self, headers, fields=None, fill=False): - """ - Get all Documents from given run(s). - - Parameters - ---------- - headers : Header or iterable of Headers - header or headers to fetch the documents for - fields : list, optional - whitelist of field names of interest; if None, all are returned - fill : bool, optional - Whether externally-stored data should be filled in. Defaults to - False. - - Yields - ------ - name, doc : tuple - string name of the Document type and the Document itself. - Example: ('start', {'time': ..., ...}) - - Examples - -------- - >>> def f(name, doc): - ... # do something - ... - >>> h = DataBroker[-1] # most recent header - >>> for name, doc in restream(h): - ... f(name, doc) - - Note - ---- - This output can be used as a drop-in replacement for the output of the - bluesky Run Engine. - - See Also - -------- - :meth:`Broker.process` - """ - for payload in self.get_documents(headers, fields=fields, fill=fill): - yield payload - - stream = restream # compat - - def process(self, headers, func, fields=None, fill=False): - """ - Pass all the documents from one or more runs into a callback. - - Parameters - ---------- - headers : Header or iterable of Headers - header or headers to process documents from - func : callable - function with the signature `f(name, doc)` - where `name` is a string and `doc` is a dict - fields : list, optional - whitelist of field names of interest; if None, all are returned - fill : bool, optional - Whether externally-stored data should be filled in. Defaults to - False. - - Examples - -------- - >>> def f(name, doc): - ... # do something - ... - >>> h = DataBroker[-1] # most recent header - >>> process(h, f) - - Note - ---- - This output can be used as a drop-in replacement for the output of the - bluesky Run Engine. - - See Also - -------- - :meth:`Broker.restream` - """ - for name, doc in self.get_documents(headers, fields=fields, fill=fill): - func(name, doc) - - get_fields = staticmethod(get_fields) # for convenience - - def export(self, headers, db, new_root=None, copy_kwargs=None): - """ - Export a list of headers. - - Parameters: - ----------- - headers : databroker.header - one or more headers that are going to be exported - db : databroker.Broker - an instance of databroker.Broker class that will be the target to - export info - new_root : str - optional. root directory of files that are going to - be exported - copy_kwargs : dict or None - passed through to the ``copy_files`` method on Registry; - None by default - - Returns - ------ - file_pairs : list - list of (old_file_path, new_file_path) pairs generated by - ``copy_files`` method on Registry. - """ - if copy_kwargs is None: - copy_kwargs = {} - try: - headers.items() - except AttributeError: - pass - else: - headers = [headers] - file_pairs = [] - for header in headers: - # insert mds - db.mds.insert_run_start(**_sanitize(header['start'])) - events = self.get_events(header) - for descriptor in header['descriptors']: - db.mds.insert_descriptor(**_sanitize(descriptor)) - for event in events: - event = event.to_name_dict_pair()[1] - # 'filled' is obtained from the descriptor, not stored - # in each event. - event.pop('filled', None) - db.mds.insert_event(**_sanitize(event)) - db.mds.insert_run_stop(**_sanitize(header['stop'])) - # insert assets - res_uids = self.get_resource_uids(header) - for uid in res_uids: - fps = self.reg.copy_files(uid, new_root=new_root, - **copy_kwargs) - file_pairs.extend(fps) - res = self.reg.resource_given_uid(uid) - new_res = db.reg.insert_resource(res['spec'], - res['resource_path'], - res['resource_kwargs'], - root=new_root) - # Note that new_res has a different resource id than res. - datums = self.reg.datum_gen_given_resource(uid) - for datum in datums: - db.reg.insert_datum(new_res, - datum['datum_id'], - datum['datum_kwargs']) - return file_pairs - - def export_size(self, headers): - """ - Get the size of files associated with a list of headers. - - Parameters: - ----------- - headers : :class:databroker.Header: - one or more headers that are going to be exported - - Returns - ------ - total_size : float - total size of all the files associated with the ``headers`` in Gb - """ - try: - headers.items() - except AttributeError: - pass - else: - headers = [headers] - total_size = 0 - for header in headers: - # get files from assets - res_uids = self.get_resource_uids(header) - for uid in res_uids: - datum_gen = self.reg.datum_gen_given_resource(uid) - datum_kwarg_gen = (datum['datum_kwargs'] for datum in - datum_gen) - files = self.reg.get_file_list(uid, datum_kwarg_gen) - for file in files: - total_size += os.path.getsize(file) - - return total_size * 1e-9 - - def fill_events(self, events, descriptors, fields=True, inplace=False): - """Fill a sequence of events - - This method will be used both inside of other `Broker` - methods and in user code. If being used with *inplace=True* then - we do not call `~Broker.prepare_hook` on the way out as either - - - we are inside another `Broker` method which will call - it on the way out - - - being called from outside and then we assume the only way - the user got an event was through another `Broker` - method, thus `~Broker.prepare_hook` has already been called - and we do not want to call it again. - - If *inplace=False* we are being called from user code and - should receive as input the result of - `~Broker.prepare_hook`. We sanitize, copy, and then re-apply - `~Broker.prepare_hook` to not change the type of the Event. - - If a field is filled, then the *filled* dict on the event is updated - to hold the datum id and the *data* dict is update to hold the data. - - Parameters - ---------- - events : Iterable[Event] - An iterable of Event documents. - - descriptors : Iterable[EventDescriptor] - An iterable of EventDescriptor documents. This must - contain the descriptor associated with each every event - you want to fill and may contain descriptors which are not - used by any of the events. - - fields : bool or Iterable[str], optional - Which fields to fill. If `True` fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is True - - inplace : bool, optional - If the input Events should be mutated inplace - - Yields - ------ - ev : Event - Event documents with filled data. - - """ - # create the processing generator - proc_gen = self._fill_events_coro(descriptors, - fields=fields, inplace=inplace) - # and prime it - proc_gen.send(None) - - try: - for ev in events: - yield proc_gen.send(ev) - finally: - proc_gen.close() - - def _fill_events_coro(self, descriptors, fields=True, inplace=False): - if fields is True: - fields = None - elif fields is False: - # if no fields, we got nothing to do! - # just yield back as-is - ev = yield - while True: - ev = yield ev - return - elif fields: - fields = set(fields) - registry_map = {} - fill_map = defaultdict(list) - for d in descriptors: - fill_keys = set() - desc_id = d['uid'] - for k, v in six.iteritems(d['data_keys']): - ext = v.get('external', None) - if ext: - # TODO sort this out! - # _, _, reg_name = ext.partition(':') - reg_name = '' - registry_map[(desc_id, k)] = self.assets[reg_name] - fill_keys.add(k) - if fields is not None: - fill_keys &= fields - fill_map[desc_id] = fill_keys - ev = yield - - while True: - if not inplace: - ev = _sanitize(ev) - ev = copy.deepcopy(ev) - ev = self.prepare_hook('event', ev) - data = ev['data'] - filled = ev['filled'] - desc_id = ev['descriptor'] - for k, v in six.iteritems(fill_map): - for dk in v: - d_id = data[dk] - data[dk] = (registry_map[(desc_id, dk)] - .retrieve(d_id)) - filled[dk] = d_id - - ev = yield ev - - def fill_table(self, table, descriptor, fields=None, inplace=False): - """Fill a table - - """ - if fields is True: - fields = None - elif fields is False: - # if no fields, we got nothing to do! - # just return the events as-is - return table - elif fields: - fields = set(fields) - # TODO unify this code with the code above. - fill_keys = set() - registry_map = {} - for k, v in six.iteritems(descriptor['data_keys']): - ext = v.get('external', None) - if ext: - # TODO sort this out! - # _, _, reg_name = ext.partition(':') - reg_name = '' - registry_map[k] = self.assets[reg_name] - fill_keys.add(k) - if fields is not None: - fill_keys &= fields - - if not inplace: - table = table.copy() - - for k in fill_keys: - reg = registry_map[k] - # TODO someday we will have bulk retrieve on assets.Registry - table[k] = [reg.retrieve(value) for value in table[k]] - - return table - - -
[docs]class Broker(BrokerES): - """ - Unified interface to data sources - - Eventually this API will change to - ``__init__(self, hs, **event_sources)`` - - Parameters - ---------- - mds : object - implementing the 'metadatastore interface' - reg : object - implementing the 'assets interface' - auto_register : boolean, optional - By default, automatically register built-in asset handlers (classes - that handle I/O for externally stored data). Set this to ``False`` - to do all registration manually. - - """ -
[docs] def __init__(self, mds, reg=None, plugins=None, filters=None, - auto_register=True): - if plugins is not None: - raise ValueError("The 'plugins' argument is no longer supported. " - "Use an EventSource instead.") - if filters is None: - filters = [] - if filters: - warnings.warn("Future versions of the databroker will not accept " - "'filters' in __init__. Set them using the filters " - "attribute after initialization.", stacklevel=2) - super(Broker, self).__init__(HeaderSourceShim(mds), - [EventSourceShim(mds, reg)], - {'': reg}) - self.filters = filters - if auto_register: - register_builtin_handlers(self.reg)
- -
[docs] @classmethod - def from_config(cls, config, auto_register=True): - """ - Create a new Broker instance using a dictionary of configuration. - - Parameters - ---------- - config : dict - auto_register : boolean, optional - By default, automatically register built-in asset handlers (classes - that handle I/O for externally stored data). Set this to ``False`` - to do all registration manually. - - Returns - ------- - db : Broker - - Examples - -------- - Create a Broker backed by sqlite databases. (This is configuration is - not recommended for large or important deployments. See the - configuration documentation for more.) - - >>> config = { - ... 'metadatastore': { - ... 'module': 'databroker.headersource.sqlite', - ... 'class': 'MDS', - ... 'config': { - ... 'directory': 'some_directory', - ... 'timezone': 'US/Eastern'} - ... }, - ... 'assets': { - ... 'module': 'databroker.assets.sqlite', - ... 'class': 'Registry', - ... 'config': { - ... 'dbpath': assets_dir + '/database.sql'} - ... } - ... } - ... - - >>> Broker.from_config(config) - """ - mds = load_component(config['metadatastore']) - assets = load_component(config['assets']) - return cls(mds, assets, auto_register=auto_register)
- -
[docs] @classmethod - def named(cls, name, auto_register=True): - """ - Create a new Broker instance using a configuration file with this name. - - Configuration file search path: - - * ``~/.config/databroker/{name}.yml`` - * ``{python}/../etc/databroker/{name}.yml`` - * ``/etc/databroker/{name}.yml`` - - where ``{python}`` is the location of the current Python binary, as - reported by ``sys.executable``. It will use the first match it finds. - - Parameters - ---------- - name : string - auto_register : boolean, optional - By default, automatically register built-in asset handlers (classes - that handle I/O for externally stored data). Set this to ``False`` - to do all registration manually. - - Returns - ------- - db : Broker - """ - db = cls.from_config(lookup_config(name), auto_register=auto_register) - return db
- - -def _sanitize(doc): - # Make this a plain dict and strip off doct.Document artifacts. - d = dict(doc) - d.pop('_name', None) - return d -
- -
-
- -
-
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/core.html b/databroker/_modules/databroker/core.html deleted file mode 100644 index bf5c3c8c40..0000000000 --- a/databroker/_modules/databroker/core.html +++ /dev/null @@ -1,2367 +0,0 @@ - - - - - - - - - - - databroker.core — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker.core
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker.core

-import collections
-import copy
-import entrypoints
-import event_model
-from datetime import datetime
-import dask
-import dask.bag
-import functools
-import heapq
-import importlib
-import itertools
-import logging
-import cachetools
-from dask.base import tokenize
-import intake.catalog.base
-import intake.catalog.local
-import intake.container.base
-from intake.compat import unpack_kwargs
-import msgpack
-import requests
-from requests.compat import urljoin
-import numpy
-import os
-import warnings
-import xarray
-
-from .intake_xarray_core.base import DataSourceMixin
-from .intake_xarray_core.xarray_container import RemoteXarray
-from .utils import LazyMap
-from bluesky_live.conversion import documents_to_xarray, documents_to_xarray_config
-from collections import deque, OrderedDict
-from dask.base import normalize_token
-
-try:
-    from intake.catalog.remote import RemoteCatalog as intake_RemoteCatalog
-except ImportError:
-    from intake.catalog.base import RemoteCatalog as intake_RemoteCatalog
-
-logger = logging.getLogger(__name__)
-
-
-class NotMutable(Exception):
-    ...
-
-
-
[docs]class Document(dict): - """ - Document is an immutable dict subclass. - - It is immutable to help consumer code avoid accidentally corrupting data - that another part of the cosumer code was expected to use unchanged. - - Subclasses of Document must define __dask_tokenize__. The tokenization - schemes typically uniquely identify the document based on only a subset of - its contents, and mutating the contents can thereby create situations where - two unequal objects have colliding tokens. Immutability helps guard against - this too. - - Note that Documents are not *recursively* immutable. Just as it is possible - create a tuple (immutable) of lists (mutable) and mutate the lists, it is - possible to mutate the internal contents of a Document, but this should not - be done. It is safer to use the to_dict() method to create a mutable deep - copy. - - This is implemented as a dict subclass in order to satisfy certain - consumers that expect an object that satisfies isinstance(obj, dict). - This implementation detail may change in the future. - """ - - __slots__ = ("__not_a_real_dict",) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # This lets pickle recognize that this is not a literal dict and that - # it should respect its custom __setstate__. - self.__not_a_real_dict = True - - def __repr__(self): - # same as dict, but wrapped in the class name so the eval round-trips - return f"{self.__class__.__name__}({dict(self)})" - - def _repr_pretty_(self, p, cycle): - """ - A multi-line but eval-able text repr with readable indentation - - This hooks into IPython/Jupyter's display mechanism - This is *not* invoked by print() or repr(), but it is invoked by - IPython.display.display() which is called in this common scenario:: - - In [1]: doc = Document(...) - In [2]: doc - <pretty representation will show here> - """ - # Note: IPython's pretty-prettying mechanism is custom and complex. - # The `text` method used below is a direct and blunt way to engage it - # and seems widely used in the IPython code base. There are other - # specific mechanisms for displaying collections like dicts, but they - # can *truncate* which I think we want to avoid and they would require - # more investment to understand how to use. - from pprint import pformat - return p.text(f"{self.__class__.__name__}({pformat(dict(self))})") - - def __getstate__(self): - return dict(self) - - def __setstate__(self, state): - dict.update(self, state) - self.__not_a_real_dict = True - - def __readonly(self, *args, **kwargs): - raise NotMutable( - "Documents are not mutable. Call the method to_dict() to make a " - "fully independent and mutable deep copy." - ) - - def __setitem__(self, key, value): - try: - self.__not_a_real_dict - except AttributeError: - # This path is necessary to support un-pickling. - return dict.__setitem__(self, key, value) - else: - self.__readonly() - - __delitem__ = __readonly - pop = __readonly - popitem = __readonly - clear = __readonly - setdefault = __readonly - update = __readonly - -
[docs] def to_dict(self): - """ - Create a mutable deep copy. - """ - # Convert to dict and then make a deep copy to ensure that if the user - # mutates any internally nested dicts there is no spooky action at a - # distance. - return copy.deepcopy(dict(self))
- - def __deepcopy__(self, memo): - # Without this, copy.deepcopy(Document(...)) fails because deepcopy - # creates a new, empty Document instance and then tries to add items to - # it. - return self.__class__({k: copy.deepcopy(v, memo) for k, v in self.items()}) - - def __dask_tokenize__(self): - raise NotImplementedError
- - -# We must use dask's registration mechanism to tell it to treat Document -# specially. Dask's tokenization dispatch mechanism discovers that Docuemnt is -# a dict subclass and treats it as a dict, ignoring its __dask_tokenize__ -# method. To force it to respect our cutsom tokenization, we must explicitly -# register it. - - -@normalize_token.register(Document) -def tokenize_document(instance): - return instance.__dask_tokenize__() - - -class Start(Document): - def __dask_tokenize__(self): - return ('start', self['uid']) - - -class Stop(Document): - def __dask_tokenize__(self): - return ('stop', self['uid']) - - -class Resource(Document): - def __dask_tokenize__(self): - return ('resource', self['uid']) - - -class Descriptor(Document): - def __dask_tokenize__(self): - return ('descriptor', self['uid']) - - -class Event(Document): - def __dask_tokenize__(self): - return ('event', self['uid']) - - -class EventPage(Document): - def __dask_tokenize__(self): - return ('event_page', self['uid']) - - -class Datum(Document): - def __dask_tokenize__(self): - return ('datum', self['datum_id']) - - -class DatumPage(Document): - def __dask_tokenize__(self): - return ('datum_page', self['uid']) - - -class PartitionIndexError(IndexError): - ... - - -class Entry(intake.catalog.local.LocalCatalogEntry): - - @property - def _pmode(self): - return 'never' - - @_pmode.setter - def _pmode(self, val): - ... - - def __init__(self, **kwargs): - # This might never come up, but just to be safe.... - if 'entry' in kwargs['args']: - raise TypeError("The args cannot contain 'entry'. It is reserved.") - super().__init__(**kwargs) - # This cache holds datasources, the result of calling super().get(...) - # with potentially different arguments. - self.__cache = self._make_cache() - self.entry = self - logger.debug("Created Entry named %r", self.name) - - @property - def catalog(self): - return self._catalog - - def _make_cache(self): - return cachetools.LRUCache(10) - - def _create_open_args(self, user_parameters): - plugin, open_args = super()._create_open_args(user_parameters) - # Inject self into arguments passed to instanitate the driver. This - # enables the driver instance to know which Entry created it. - open_args['entry'] = self - return plugin, open_args - - def cache_clear(self): - self.__cache.clear() - - def get(self, **kwargs): - token = tokenize(OrderedDict(kwargs)) - try: - datasource = self.__cache[token] - logger.debug( - "Entry cache found %s named %r", - datasource.__class__.__name__, - datasource.name) - except KeyError: - datasource = super().get(**kwargs) - self.__cache[token] = datasource - return datasource - - # def __dask_tokenize__(self): - # print('bob') - # metadata = self.describe()['metadata'] - # return ('Entry', metadata['start']['uid']) - - -class StreamEntry(Entry): - """ - This is a temporary fix that is being proposed to include in intake. - """ - def _make_cache(self): - return dict() - - # def __dask_tokenize__(self): - # print('bill') - # metadata = self.describe()['metadata'] - # print(self.describe()) - # return ('Stream', metadata['start']['uid'], self.name) - - -def to_event_pages(get_event_cursor, page_size): - """ - Decorator that changes get_event_cursor to get_event_pages. - - get_event_cursor yields events, get_event_pages yields event_pages. - - Parameters - ---------- - get_event_cursor : function - - Returns - ------- - get_event_pages : function - """ - - @functools.wraps(get_event_cursor) - def get_event_pages(*args, **kwargs): - event_cursor = get_event_cursor(*args, **kwargs) - while True: - result = list(itertools.islice(event_cursor, page_size)) - if result: - yield event_model.pack_event_page(*result) - else: - break - - return get_event_pages - - -def to_datum_pages(get_datum_cursor, page_size): - """ - Decorator that changes get_datum_cursor to get_datum_pages. - - get_datum_cursor yields datum, get_datum_pages yields datum_pages. - - Parameters - ---------- - get_datum_cursor : function - - Returns - ------- - get_datum_pages : function - """ - - @functools.wraps(get_datum_cursor) - def get_datum_pages(*args, **kwargs): - datum_cursor = get_datum_cursor(*args, **kwargs) - while True: - result = list(itertools.islice(datum_cursor, page_size)) - if result: - yield event_model.pack_datum_page(*result) - else: - break - - return get_datum_pages - - -def retry(function): - """ - Decorator that retries a Catalog function once. - - Parameters - ---------- - function: function - - Returns - ------- - new_function: function - """ - - @functools.wraps(function) - def new_function(self, *args, **kwargs): - try: - return function(self, *args, **kwargs) - except Exception: - self.force_reload() - return function(self, *args, **kwargs) - - return new_function - - -def _flatten_event_page_gen(gen): - """ - Converts an event_page generator to an event generator. - - Parameters - ---------- - gen : generator - - Returns - ------- - event_generator : generator - """ - for page in gen: - yield from event_model.unpack_event_page(page) - - -def _interlace_event_pages(*gens): - """ - Take event_page generators and interlace their results by timestamp. - This is a modification of https://github.com/bluesky/databroker/pull/378/ - - Parameters - ---------- - gens : generators - Generators of (name, dict) pairs where the dict contains a 'time' key. - Yields - ------ - val : tuple - The next (name, dict) pair in time order - - """ - iters = [iter(g) for g in gens] - heap = [] - - def safe_next(index): - try: - val = next(iters[index]) - except StopIteration: - return - heapq.heappush(heap, (val['time'][0], val['uid'][0], index, val)) - - for i in range(len(iters)): - safe_next(i) - - while heap: - _, _, index, val = heapq.heappop(heap) - yield val - safe_next(index) - - -def _interlace_event_page_chunks(*gens, chunk_size): - """ - Take event_page generators and interlace their results by timestamp. - - This is a modification of https://github.com/bluesky/databroker/pull/378/ - - Parameters - ---------- - gens : generators - Generators of (name, dict) pairs where the dict contains a 'time' key. - chunk_size : integer - Size of pages to yield - Yields - ------ - val : tuple - The next (name, dict) pair in time order - - """ - iters = [iter(event_model.rechunk_event_pages(g, chunk_size)) for g in gens] - yield from _interlace_event_pages(*iters) - - -def _interlace(*gens, strict_order=True): - """ - Take event_page generators and interlace their results by timestamp. - - This is a modification of https://github.com/bluesky/databroker/pull/378/ - - Parameters - ---------- - gens : generators - Generators of (name, dict) pairs where the dict contains a 'time' key. - strict_order : bool, optional - documents are strictly yielded in ascending time order. Defaults to - True. - Yields - ------ - val : tuple - The next (name, dict) pair in time order - - """ - iters = [iter(g) for g in gens] - heap = [] - fifo = deque() - - # Gets the next event/event_page from the iterator iters[index], while - # appending documents that are not events/event_pages to the fifo. - def get_next(index): - while True: - try: - name, doc = next(iters[index]) - except StopIteration: - return - if name == 'event': - heapq.heappush(heap, (doc['time'], doc['uid'], index, (name, doc))) - return - elif name == 'event_page': - if strict_order: - for event in event_model.unpack_event_page(doc): - event_page = event_model.pack_event_page(event) - heapq.heappush(heap, (event_page['time'][0], event_page['uid'][0], - index, ('event_page', event_page))) - return - else: - heapq.heappush(heap, (doc['time'][0], doc['uid'][0], index, (name, doc))) - return - else: - if name not in ['start', 'stop']: - fifo.append((name, doc)) - - # Put the next event/event_page from each generator in the heap. - for i in range(len(iters)): - get_next(i) - - # First yield docs that are not events or event pages from the fifo queue, - # and then yield from the heap. We can improve this by keeping a count of - # the number of documents from each stream in the heap, and only calling - # get_next when the count is 0. As is the heap could potentially get very - # large. - while heap: - while fifo: - yield fifo.popleft() - _, _, index, doc = heapq.heappop(heap) - yield doc - get_next(index) - - # Yield any remaining items in the fifo queue. - while fifo: - yield fifo.popleft() - - -def _unfilled_partitions(start, descriptors, resources, stop, datum_gens, - event_gens, partition_size): - """ - Return a Bluesky run, in order, packed into partitions. - - Parameters - ---------- - start : dict - Bluesky run_start document - descriptors: list - List of Bluesky descriptor documents - resources: list - List of Bluesky resource documents - stop: dict - Bluesky run_stop document - datum_gens : generators - Generators of datum_pages. - event_gens : generators - Generators of (name, dict) pairs where the dict contains a 'time' key. - partition_size : integer - Size of partitions to yield - chunk_size : integer - Size of pages to yield - - Yields - ------ - partition : list - List of lists of (name, dict) pair in time order - """ - # The first partition is the "header" - yield ([('start', start)] - + [('descriptor', doc) for doc in descriptors] - + [('resource', doc) for doc in resources]) - - # Use rechunk datum pages to make them into pages of size "partition_size" - # and yield one page per partition. - for datum_gen in datum_gens: - partition = [('datum_page', datum_page) for datum_page in - event_model.rechunk_datum_pages(datum_gen, partition_size)] - if partition: - yield partition - - # Rechunk the event pages and interlace them in timestamp order, then pack - # them into a partition. - count = 0 - partition = [] - for event_page in _interlace_event_pages(*event_gens): - partition.append(('event_page', event_page)) - count += 1 - if count == partition_size: - yield partition - count = 0 - partition = [] - - # Add the stop document onto the last partition. - partition.append(('stop', stop)) - yield partition - - -def _fill(filler, - event, - lookup_resource_for_datum, - get_resource, - get_datum_pages, - last_datum_id=None): - try: - _, filled_event = filler("event", event) - return filled_event - except event_model.UnresolvableForeignKeyError as err: - datum_id = err.key - if datum_id == last_datum_id: - # We tried to fetch this Datum on the last trip - # trip through this method, and apparently it did not - # work. We are in an infinite loop. Bail! - raise - - # try to fast-path looking up the resource uid if this works - # it saves us a a database hit (to get the datum document) - if "/" in datum_id: - resource_uid, _ = datum_id.split("/", 1) - # otherwise do it the standard way - else: - resource_uid = lookup_resource_for_datum(datum_id) - - # but, it might be the case that the key just happens to have - # a '/' in it and it does not have any semantic meaning so we - # optimistically try - try: - resource = get_resource(uid=resource_uid) - # and then fall back to the standard way to be safe - except ValueError: - resource = get_resource(lookup_resource_for_datum(datum_id)) - - filler("resource", resource) - # Pre-fetch all datum for this resource. - for datum_page in get_datum_pages(resource_uid=resource_uid): - filler("datum_page", datum_page) - # TODO -- When to clear the datum cache in filler? - - # Re-enter and try again now that the Filler has consumed the - # missing Datum. There might be another missing Datum in this same - # Event document (hence this re-entrant structure) or might be good - # to go. - return _fill( - filler, - event, - lookup_resource_for_datum, - get_resource, - get_datum_pages, - last_datum_id=datum_id, - ) - - -def _documents(*, start, stop, entries, fill, strict_order=True): - """ - Yields documents from this Run in chronological order. - - Parameters - ---------- - start_doc : dict - RunStart Document - stop_doc : dict - RunStop Document - entries : dict - A dict of the BlueskyRun's entries. - fill: {'yes', 'no'} - If fill is 'yes', any external data referenced by Event documents - will be filled in (e.g. images as numpy arrays). This is typically - the desired option for *using* the data. - If fill is 'no', the Event documents will contain foreign keys as - placeholders for the data. This option is useful for exporting - copies of the documents. - strict_order : bool, optional - documents are strictly yielded in ascending time order. - """ - history = set() - - FILL_OPTIONS = {'yes', 'no', 'delayed'} - if fill not in FILL_OPTIONS: - raise ValueError(f"Invalid fill option: {fill}, fill must be: {FILL_OPTIONS}") - - def stream_gen(entry): - for i in itertools.count(): - partition = entry().read_partition({'index': i, 'fill': fill, - 'partition_size': 'auto'}) - if not partition: - break - yield from partition - - streams = [stream_gen(entry) for entry in entries.values()] - yield ('start', start) - - # This following code filters out duplicate documents. - # This is needed because we dont know which EventStream, that resource - # or datum documents belong to, so each stream has these documents. - # Without this filter we would get multiple of the same resource and - # datum documents. - for name, doc in _interlace(*streams, strict_order=strict_order): - - if name == 'datum': - if doc['datum_id'] not in history: - yield (name, doc) - history.add(doc['datum_id']) - - if name == 'datum_page': - if tuple(doc['datum_id']) not in history: - yield (name, doc) - history.add(tuple(doc['datum_id'])) - - elif name == 'resource': - if doc['uid'] not in history: - yield (name, doc) - history.add(doc['uid']) - - else: - yield (name, doc) - if stop is not None: - yield ('stop', stop) - - -
[docs]class RemoteBlueskyRun(intake_RemoteCatalog): - """ - Catalog representing one Run. - - This is a client-side proxy to a BlueskyRun stored on a remote server. - - Parameters - ---------- - url: str - Address of the server - headers: dict - HTTP headers to sue in calls - name: str - handle to reference this data - parameters: dict - To pass to the server when it instantiates the data source - metadata: dict - Additional info - kwargs: ignored - """ - name = 'bluesky-run' - - # opt-out of the persistence features of intake - - @property - def has_been_persisted(self): - return False - - @property - def is_persisted(self): - return False - - def get_persisted(self): - raise KeyError("Does not support intake persistence") - -
[docs] def persist(self, *args, **kwargs): - raise NotImplementedError
- - @property - def pmode(self): - return 'never' - - @pmode.setter - def pmode(self, val): - ... - - # def __dask_tokenize__(self): - # print('baz') - # return ('RemoteBlueskyRun', self.metadata['start']['uid']) - - def __init__(self, url, http_args, name, parameters, metadata=None, **kwargs): - self.url = url - self.name = name - self.parameters = parameters - self.http_args = http_args - self._source_id = None - self.metadata = metadata or {} - response = self._get_source_id() - self.bag = None - self._source_id = response['source_id'] - super().__init__(url=url, http_args=http_args, name=name, - metadata=metadata, - source_id=self._source_id) - # turn off any attempts at persistence - self._pmode = "never" - self.npartitions = response['npartitions'] - self.metadata = response['metadata'] - self._schema = intake.source.base.Schema( - datashape=None, dtype=None, - shape=self.shape, - npartitions=self.npartitions, - metadata=self.metadata) - - def _get_source_id(self): - if self._source_id is None: - payload = dict(action='open', name=self.name, - parameters=self.parameters) - req = requests.post(urljoin(self.url, '/v1/source'), - data=msgpack.packb(payload, use_bin_type=True), - **self.http_args) - req.raise_for_status() - response = msgpack.unpackb(req.content, **unpack_kwargs) - return response - - def _load_metadata(self): - return self._schema - - def _get_partition(self, partition): - return intake.container.base.get_partition(self.url, self.http_args, - self._source_id, self.container, - partition) - -
[docs] def read(self): - raise NotImplementedError( - "Reading the BlueskyRun itself is not supported. Instead read one " - "its entries, representing individual Event Streams.")
- -
[docs] def to_dask(self): - raise NotImplementedError( - "Reading the BlueskyRun itself is not supported. Instead read one " - "its entries, representing individual Event Streams.")
- - def _close(self): - self.bag = None - - def documents(self, *, fill, strict_order=True): - # Special case for 'delayed' since it *is* supported in the local mode - # of usage. - if fill == 'delayed': - raise NotImplementedError( - "Delayed access is not yet supported via the client--server " - "usage.") - - yield from _documents(start=self.metadata['start'], - stop=self.metadata['stop'], - entries=self._entries, - fill=fill, - strict_order=strict_order) - - def read_canonical(self): - warnings.warn( - "The method read_canonical has been renamed documents. This alias " - "may be removed in a future release.") - yield from self.documents(fill='yes') - - def canonical(self): - warnings.warn( - "The method canonical has been renamed documents. This alias " - "may be removed in a future release.") - yield from self.documents(fill='yes') - - def __repr__(self): - try: - self._load() - start = self.metadata['start'] - return f"<{self.__class__.__name__} uid={start['uid']!r}>" - except Exception as exc: - return f"<{self.__class__.__name__} *REPR RENDERING FAILURE* {exc!r}>" - - def _repr_pretty_(self, p, cycle): - try: - self._load() - start = self.metadata['start'] - stop = self.metadata['stop'] - out = (f"BlueskyRun\n" - f" uid={start['uid']!r}\n" - f" exit_status={stop.get('exit_status')!r}\n" - f" {_ft(start['time'])} -- {_ft(stop.get('time', '?'))}\n" - f" Streams:\n") - for stream_name in self: - out += f" * {stream_name}\n" - except Exception as exc: - out = f"<{self.__class__.__name__} *REPR_RENDERING_FAILURE* {exc!r}>" - p.text(out) - - def search(self): - raise NotImplementedError("Cannot search within one run.")
- - -
[docs]class BlueskyRun(intake.catalog.Catalog): - """ - Catalog representing one Run. - - Parameters - ---------- - get_run_start: callable - Expected signature ``get_run_start() -> RunStart`` - get_run_stop : callable - Expected signature ``get_run_stop() -> RunStop`` - get_event_descriptors : callable - Expected signature ``get_event_descriptors() -> List[EventDescriptors]`` - get_event_pages : callable - Expected signature ``get_event_pages(descriptor_uid) -> generator`` - where ``generator`` yields Event documents - get_event_count : callable - Expected signature ``get_event_count(descriptor_uid) -> int`` - get_resource : callable - Expected signature ``get_resource(resource_uid) -> Resource`` - get_resources: callable - Expected signature ``get_resources() -> Resources`` - lookup_resource_for_datum : callable - Expected signature ``lookup_resource_for_datum(datum_id) -> resource_uid`` - get_datum_pages : callable - Expected signature ``get_datum_pages(resource_uid) -> generator`` - where ``generator`` yields Datum documents - get_filler : callable - Expected signature ``get_filler() -> event_model.Filler`` - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - # Work around - # https://github.com/intake/intake/issues/545 - _container = None - - # opt-out of the persistence features of intake - @property - def has_been_persisted(self): - return False - - @property - def is_persisted(self): - return False - - def get_persisted(self): - raise KeyError("Does not support intake persistence") - -
[docs] def persist(self, *args, **kwargs): - raise NotImplementedError
- - @property - def pmode(self): - return 'never' - - @pmode.setter - def pmode(self, val): - ... - - # def __dask_tokenize__(self): - # print('baz') - # return ('BlueksyRun', self.metadata['start']['uid']) - - container = 'bluesky-run' - version = '0.0.1' - partition_access = True - PARTITION_SIZE = 100 - - def __init__(self, - get_run_start, - get_run_stop, - get_event_descriptors, - get_event_pages, - get_event_count, - get_resource, - get_resources, - lookup_resource_for_datum, - get_datum_pages, - get_filler, - entry, - transforms, - **kwargs): - # Set the name here, earlier than the base class does, so that the log - # message in self._load has access to it. - # All **kwargs are passed up to base class. TODO: spell them out - # explicitly. - self.urlpath = '' # TODO Not sure why I had to add this. - self._get_run_start = get_run_start - self._get_run_stop = get_run_stop - self._get_event_descriptors = get_event_descriptors - self._get_event_pages = get_event_pages - self._get_event_count = get_event_count - self._get_resource = get_resource - self._get_resources = get_resources - self._lookup_resource_for_datum = lookup_resource_for_datum - self._get_datum_pages = get_datum_pages - self.fillers = {} - self.fillers['yes'] = get_filler(coerce='force_numpy') - self.fillers['no'] = event_model.NoFiller( - self.fillers['yes'].handler_registry, inplace=True) - self.fillers['delayed'] = get_filler(coerce='delayed') - self._transforms = transforms - self._run_stop_doc = None - self.__entry = entry - super().__init__(**{**kwargs, 'persist_mode': 'never'}) - # turn off any attempts at persistence - self._pmode = "never" - logger.debug( - "Created %s named %r", - self.__class__.__name__, - entry.name) - -
[docs] def describe(self): - return self.__entry.describe()
- - def __repr__(self): - try: - self._load() - start = self.metadata['start'] - return f"<{self.__class__.__name__} uid={start['uid']!r}>" - except Exception as exc: - return f"<{self.__class__.__name__} *REPR RENDERING FAILURE* {exc!r}>" - - def _repr_pretty_(self, p, cycle): - try: - self._load() - start = self.metadata['start'] - stop = self.metadata['stop'] - out = (f"BlueskyRun\n" - f" uid={start['uid']!r}\n" - f" exit_status={stop.get('exit_status')!r}\n" - f" {_ft(start['time'])} -- {_ft(stop.get('time', '?'))}\n" - f" Streams:\n") - for stream_name in self: - out += f" * {stream_name}\n" - except Exception as exc: - out = f"<{self.__class__.__name__} *REPR_RENDERING_FAILURE* {exc!r}>" - p.text(out) - - def _make_entries_container(self): - return LazyMap() - - def _load(self): - self._run_start_doc = Start(self._transforms['start'](self._get_run_start())) - - # get_run_stop() may return None if the document was never created due - # to a critical failure or simply not yet emitted during a Run that is - # still in progress. If it returns None, pass that through. - if self._run_stop_doc is None: - stop = self._get_run_stop() - if stop is None: - self._run_stop_doc = stop - else: - self._run_stop_doc = Stop(self._transforms['stop'](stop)) - - self.metadata.update({'start': self._run_start_doc}) - self.metadata.update({'stop': self._run_stop_doc}) - - # TODO Add driver API to allow us to fetch just the stream names not - # all the descriptors. We don't need them until BlueskyEventStream. - self._descriptors = [self._transforms['descriptor'](descriptor) - for descriptor in self._get_event_descriptors()] - - # Count the total number of documents in this run. - count = 1 - descriptor_uids = [doc['uid'] for doc in self._descriptors] - count += len(descriptor_uids) - for doc in self._descriptors: - count += self._get_event_count(doc['uid']) - count += (self._run_stop_doc is not None) - - self._schema = intake.source.base.Schema( - datashape=None, - dtype=None, - shape=(count,), - npartitions=self.npartitions, - metadata=self.metadata) - - # Make a BlueskyEventStream for each stream_name. - for doc in self._descriptors: - if 'name' not in doc: - warnings.warn( - f"EventDescriptor {doc['uid']!r} has no 'name', likely " - f"because it was generated using an old version of " - f"bluesky. The name 'primary' will be used.") - stream_names = set(doc.get('name', 'primary') for doc in self._descriptors) - new_stream_names = stream_names - set(self._entries) - - def wrapper(stream_name, metadata, args): - return StreamEntry(name=stream_name, - description={}, # TODO - driver='databroker.core.BlueskyEventStream', - direct_access='forbid', - args=args, - cache=None, # What does this do? - metadata=metadata, - catalog_dir=None, - getenv=True, - getshell=True, - catalog=self) - - # We employ OrderedDict in several places in this loop. The motivation - # is to speed up dask tokenization. When dask tokenizes a plain dict, - # it sorts the keys, and it turns out that this sort operation - # dominates the call time, even for very small dicts. Using an - # OrderedDict steers dask toward a different and faster tokenization. - new_entries = {} - for stream_name in new_stream_names: - metadata = OrderedDict({'start': self.metadata['start'], - 'stop': self.metadata['stop']}) - args = OrderedDict( - stream_name=stream_name, - get_run_stop=self._get_run_stop, - get_event_descriptors=self._get_event_descriptors, - get_event_pages=self._get_event_pages, - get_event_count=self._get_event_count, - get_resource=self._get_resource, - get_resources=self._get_resources, - lookup_resource_for_datum=self._lookup_resource_for_datum, - get_datum_pages=self._get_datum_pages, - fillers=OrderedDict(self.fillers), - transforms=OrderedDict(self._transforms), - metadata=metadata) - - new_entries[stream_name] = functools.partial(wrapper, stream_name, - metadata, args) - self._entries.add(new_entries) - logger.debug( - "Loaded %s named %r", - self.__class__.__name__, - self.__entry.name) - -
[docs] def configure_new(self, **kwargs): - """ - Return self or, if args are provided, some new instance of type(self). - - This is here so that the user does not have to remember whether a given - variable is a BlueskyRun or an *Entry* with a Bluesky Run. In either - case, ``obj()`` will return a BlueskyRun. - """ - return self.__entry.get(**kwargs)
- - get = __call__ = configure_new - - def documents(self, *, fill, strict_order=True): - yield from _documents(start=self.metadata['start'], - stop=self.metadata['stop'], - entries=self._entries, - fill=fill, - strict_order=strict_order) - - def read_canonical(self): - warnings.warn( - "The method read_canonical has been renamed documents. This alias " - "may be removed in a future release.") - yield from self.documents(fill='yes') - - def canonical(self): - warnings.warn( - "The method canonical has been renamed documents. This alias " - "may be removed in a future release.") - yield from self.documents(fill='yes') - -
[docs] def get_file_list(self, resource): - """ - Fetch filepaths of external files associated with this Run. - - This method is not defined on RemoteBlueskyRun because the filepaths - may not be meaningful on a remote machine. - - This method should be considered experimental. It may be changed or - removed in a future release. - """ - files = [] - handler = self.fillers['yes'].get_handler(resource) - - def datum_kwarg_gen(): - for page in self._get_datum_pages(resource['uid']): - for datum in event_model.unpack_datum_page(page): - yield datum['datum_kwargs'] - - files.extend(handler.get_file_list(datum_kwarg_gen())) - return files
- -
[docs] def read(self): - raise NotImplementedError( - "Reading the BlueskyRun itself is not supported. Instead read one " - "its entries, representing individual Event Streams. You can see " - "the entries using list(YOUR_VARIABLE_HERE). Tab completion may " - - "also help, if available.")
- -
[docs] def to_dask(self): - raise NotImplementedError( - "Reading the BlueskyRun itself is not supported. Instead read one " - "its entries, representing individual Event Streams. You can see " - "the entries using list(YOUR_VARIABLE_HERE). Tab completion may " - "also help, if available.")
- - -
[docs]class BlueskyEventStream(DataSourceMixin): - """ - Catalog representing one Event Stream from one Run. - - Parameters - ---------- - stream_name : string - Stream name, such as 'primary'. - get_run_stop : callable - Expected signature ``get_run_stop() -> RunStop`` - get_event_descriptors : callable - Expected signature ``get_event_descriptors() -> List[EventDescriptors]`` - get_event_pages : callable - Expected signature ``get_event_pages(descriptor_uid) -> generator`` - where ``generator`` yields event_page documents - get_event_count : callable - Expected signature ``get_event_count(descriptor_uid) -> int`` - get_resource : callable - Expected signature ``get_resource(resource_uid) -> Resource`` - get_resources: callable - Expected signature ``get_resources() -> Resources`` - lookup_resource_for_datum : callable - Expected signature ``lookup_resource_for_datum(datum_id) -> resource_uid`` - get_datum_pages : callable - Expected signature ``get_datum_pages(resource_uid) -> generator`` - where ``generator`` yields datum_page documents - fillers : dict of Fillers - transforms : Dict[str, Callable] - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - metadata : dict - passed through to base class - include : list, optional - Fields ('data keys') to include. By default all are included. This - parameter is mutually exclusive with ``exclude``. - exclude : list, optional - Fields ('data keys') to exclude. By default none are excluded. This - parameter is mutually exclusive with ``include``. - sub_dict : {"data", "timestamps"}, optional - Which sub-dict in the EventPage to use - configuration_for : str - The name of an object (e.g. device) whose configuration we want to - read. - **kwargs : - Additional keyword arguments are passed through to the base class. - """ - - # def __dask_tokenize__(self): - # print('bill') - # intake_desc = self.describe() - # return ('Stream', intake_desc['metadata']['start']['uid'], metadata['name']) - # opt-out of the persistence features of intake - @property - def has_been_persisted(self): - return False - - @property - def is_persisted(self): - return False - - def get_persisted(self): - raise KeyError("Does not support intake persistence") - -
[docs] def persist(self, *args, **kwargs): - raise NotImplementedError
- - @property - def _pmode(self): - return 'never' - - @_pmode.setter - def _pmode(self, val): - ... - - container = 'bluesky-event-stream' - version = '0.0.1' - partition_access = True - - def __init__(self, - stream_name, - get_run_stop, - get_event_descriptors, - get_event_pages, - get_event_count, - get_resources, - get_resource, - lookup_resource_for_datum, - get_datum_pages, - fillers, - transforms, - metadata, - entry, - include=None, - exclude=None, - sub_dict="data", - configuration_for=None, - **kwargs): - - self._stream_name = stream_name - self._get_event_descriptors = get_event_descriptors - self._get_run_stop = get_run_stop - self._get_event_pages = get_event_pages - self._get_event_count = get_event_count - self._get_resources = get_resources - self._get_resource = get_resource - self._lookup_resource_for_datum = lookup_resource_for_datum - self._get_datum_pages = get_datum_pages - self.fillers = fillers - self._transforms = transforms - self.urlpath = '' # TODO Not sure why I had to add this. - self._ds = None # set by _open_dataset below - self.include = include - self.exclude = exclude - if sub_dict not in {"data", "timestamps"}: - raise ValueError( - "The parameter 'sub_dict' controls where the xarray should " - "contain the Events' 'data' (common case) or reading-specific " - "'timestamps' (sometimes needed for hardware debugging). It " - f"must be one of those two strings, not {sub_dict}.") - self._configuration_for = configuration_for - self._sub_dict = sub_dict - self._partitions = None - self.__entry = entry - - super().__init__(metadata=metadata, **kwargs) - # turn off any attempts at persistence - self._pmode = "never" - self._run_stop_doc = metadata['stop'] - self._run_start_doc = metadata['start'] - self._load_header() - logger.debug( - "Created %s for stream name %r", - self.__class__.__name__, - self._stream_name) - - def _load_header(self): - # TODO Add driver API to fetch only the descriptors of interest instead - # of fetching all of them and then filtering. - self._descriptors = d = [Descriptor(self._transforms['descriptor'](descriptor)) - for descriptor in self._get_event_descriptors() - if descriptor.get('name') == self._stream_name] - self.metadata.update({'descriptors': d}) - # TODO Should figure out a way so that self._resources doesn't have to - # be all of the Run's resources. - # TDOO Should we expose this in metadata as well? Since - # _get_resources() only discovers new-style Resources that have a - # run_start in them, leave it private for now. - self._resources = [Resource(self._transforms['resource'](resource)) - for resource in self._get_resources()] - - # get_run_stop() may return None if the document was never created due - # to a critical failure or simply not yet emitted during a Run that is - # still in progress. If it returns None, pass that through. - if self._run_stop_doc is None: - stop = self._get_run_stop() - if stop is not None: - self._run_stop_doc = s = Stop(self._transforms['stop'](stop)) - self.metadata.update({'stop': s}) - logger.debug( - "Loaded %s for stream name %r", - self.__class__.__name__, - self._stream_name) - - def __repr__(self): - try: - out = (f"<{self.__class__.__name__} {self._stream_name!r} " - f"from Run {self._run_start_doc['uid'][:8]}...>") - except Exception as exc: - out = f"<{self.__class__.__name__} *REPR_RENDERING_FAILURE* {exc!r}>" - return out - - def _open_dataset(self): - self._load_header() - if self._configuration_for is not None: - self._ds = documents_to_xarray_config( - object_name=self._configuration_for, - sub_dict=self._sub_dict, - start_doc=self._run_start_doc, - stop_doc=self._run_stop_doc, - descriptor_docs=self._descriptors, - get_event_pages=self._get_event_pages, - filler=self.fillers['delayed'], - get_resource=self._get_resource, - lookup_resource_for_datum=self._lookup_resource_for_datum, - get_datum_pages=self._get_datum_pages, - include=self.include, - exclude=self.exclude) - else: - self._ds = documents_to_xarray( - sub_dict=self._sub_dict, - start_doc=self._run_start_doc, - stop_doc=self._run_stop_doc, - descriptor_docs=self._descriptors, - get_event_pages=self._get_event_pages, - filler=self.fillers['delayed'], - get_resource=self._get_resource, - lookup_resource_for_datum=self._lookup_resource_for_datum, - get_datum_pages=self._get_datum_pages, - include=self.include, - exclude=self.exclude) - -
[docs] def read(self): - """ - Return data from this Event Stream as an xarray.Dataset. - - This loads all of the data into memory. For delayed ("lazy"), chunked - access to the data, see :meth:`to_dask`. - """ - # Implemented just so we can put in a docstring - return super().read()
- -
[docs] def to_dask(self): - """ - Return data from this Event Stream as an xarray.Dataset backed by dask. - """ - # Implemented just so we can put in a docstring - return super().to_dask()
- - def _load_partitions(self, partition_size): - self._load_header() - datum_gens = [self._get_datum_pages(resource['uid']) - for resource in self._resources] - event_gens = [list(self._get_event_pages(descriptor['uid'])) - for descriptor in self._descriptors] - self._partitions = list( - _unfilled_partitions(self._run_start_doc, self._descriptors, - self._resources, self._run_stop_doc, - datum_gens, event_gens, partition_size)) - self.npartitions = len(self._partitions) - -
[docs] def read_partition(self, partition): - """Fetch one chunk of documents. - """ - if isinstance(partition, (tuple, list)): - return super().read_partition(partition) - - # Unpack partition - i = partition['index'] - - if isinstance(partition['fill'], str): - filler = self.fillers[partition['fill']] - else: - filler = partition['fill'] - - # Partition size is the number of pages in the partition. - if partition['partition_size'] == 'auto': - partition_size = 5 - elif isinstance(partition['partition_size'], int): - partition_size = partition['partition_size'] - else: - raise ValueError(f"Invalid partition_size {partition['partition_size']}") - - if self._partitions is None: - self._load_partitions(partition_size) - try: - try: - return [filler(name, doc) for name, doc in self._partitions[i]] - except event_model.UnresolvableForeignKeyError as err: - # Slow path: This error should only happen if there is an old style - # resource document that doesn't have a run_start key. - self._partitions[i:i] = self._missing_datum(err.key, partition_size) - return [filler(name, doc) for name, doc in self._partitions[i]] - except IndexError: - return []
- - def _missing_datum(self, datum_id, partition_size): - # Get the resource from the datum_id. - if '/' in datum_id: - resource_uid, _ = datum_id.split('/', 1) - else: - resource_uid = self._lookup_resource_for_datum(datum_id) - resource = self._get_resource(uid=resource_uid) - - # Use rechunk datum pages to make them into pages of size "partition_size" - # and yield one page per partition. Rechunk might be slow. - datum_gen = self._get_datum_pages(resource['uid']) - partitions = [[('datum_page', datum_page)] for datum_page in - event_model.rechunk_datum_pages(datum_gen, partition_size)] - - # Check that the datum_id from the exception has been added. - def check(): - for partition in partitions: - for name, datum_page in partition: - if datum_id in datum_page['datum_id']: - return True - return False - - if not check(): - raise - - # Add the resource to the begining of the first partition. - partitions[0] = [('resource', resource)] + partitions[0] - self.npartitions += len(partitions) - return partitions - - def _get_partition(self, partition): - return intake.container.base.get_partition( - self.url, self.http_args, - self._source_id, self.container, - partition) - - @property - def config(self): - objects = set() - for d in self._descriptors: - objects.update(set(d["object_keys"])) - return intake.catalog.Catalog.from_dict( - {object_name: self.configure_new(configuration_for=object_name) - for object_name in objects} - ) - - @property - def config_timestamps(self): - objects = set() - for d in self._descriptors: - objects.update(set(d["object_keys"])) - return intake.catalog.Catalog.from_dict( - {object_name: self.configure_new(configuration_for=object_name, - sub_dict="timestamps") - for object_name in objects} - ) - - @property - def timestamps(self): - return self.configure_new(sub_dict="timestamps") - -
[docs] def configure_new(self, **kwargs): - """ - Return self or, if args are provided, some new instance of type(self). - - This is here so that the user does not have to remember whether a given - variable is a BlueskyRun or an *Entry* with a Bluesky Run. In either - case, ``obj()`` will return a BlueskyRun. - """ - return self.__entry.get(**kwargs)
- - -class RemoteBlueskyEventStream(RemoteXarray): - # Because of the container_map, when working in remote mode, when accessing - # a BlueskyRun or BlueskyEventStream, you will get a RemoteBlueskyRun or a - # RemoteBlueskyEventStream on the client side. Canonical of the RemoteBlueskyRun, - # calls read_partition of the RemoteBlueskyEventStream, where there - # partition argument is a dict. The inherited read_partition method only - # accepts an integer for the partition argument, so read_partition needs to - # be overridden. - def read_partition(self, partition): - self._load_metadata() - return self._get_partition(partition) - - def __repr__(self): - try: - out = (f"<{self.__class__.__name__} {self._stream_name!r} " - f"from Run {self._run_start_doc['uid'][:8]}...>") - except Exception as exc: - out = f"<{self.__class__.__name__} *REPR_RENDERING_FAILURE* {exc!r}>" - return out - - -class DocumentCache(event_model.DocumentRouter): - def __init__(self): - self.descriptors = {} - self.resources = {} - self.event_pages = collections.defaultdict(list) - self.datum_pages_by_resource = collections.defaultdict(list) - self.resource_uid_by_datum_id = {} - self.start_doc = None - self.stop_doc = None - - def start(self, doc): - self.start_doc = doc - - def stop(self, doc): - self.stop_doc = doc - - def event_page(self, doc): - self.event_pages[doc['descriptor']].append(doc) - - def datum_page(self, doc): - self.datum_pages_by_resource[doc['resource']].append(doc) - for datum_id in doc['datum_id']: - self.resource_uid_by_datum_id[datum_id] = doc['resource'] - - def descriptor(self, doc): - self.descriptors[doc['uid']] = doc - - def resource(self, doc): - self.resources[doc['uid']] = doc - - -class SingleRunCache: - """ - Collect the document from one Run and, when complete, provide a BlueskyRun. - - Parameters - ---------- - handler_registry: dict, optional - This is passed to the Filler or whatever class is given in the - filler_class parameter below. - - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - - A 'handler class' may be any callable with the signature:: - - handler_class(resource_path, root, **resource_kwargs) - - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - - handler_instance(**datum_kwargs) - - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the filler_class - parameter below. - - str -> str mapping to account for temporarily moved/copied/remounted - files. Any resources which have a ``root`` in ``root_map`` will be - loaded using the mapped ``root``. - filler_class: type - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the same - methods as ``DocumentRouter``. - transforms: dict - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - - Examples - -------- - Subscribe to a document stream from within a plan. - >>> def plan(): - ... src = SingleRunCache() - ... - ... @bluesky.preprocessors.subs_decorator(src.callback) - ... def inner_plan(): - ... yield from bluesky.plans.rel_scan(...) - ... run = src.retrieve() - ... table = run.primary.read().to_dataframe() - ... ... - ... - ... yield from inner_plan() - - """ - def __init__(self, *, handler_registry=None, root_map=None, - filler_class=event_model.Filler, transforms=None): - - self._root_map = root_map or {} - self._filler_class = filler_class - self._transforms = parse_transforms(transforms) - if handler_registry is None: - handler_registry = discover_handlers() - self._handler_registry = parse_handler_registry(handler_registry) - self.handler_registry = event_model.HandlerRegistryView( - self._handler_registry) - - self._get_filler = functools.partial( - self._filler_class, - handler_registry=self.handler_registry, - root_map=self._root_map, - inplace=False) - self._collector = deque() # will contain (name, doc) pairs - self._complete = False # set to Run Start uid when stop doc is received - self._run = None # Cache BlueskyRun instance here. - - def callback(self, name, doc): - """ - Subscribe to a document stream. - """ - if self._complete: - raise ValueError( - "Already received 'stop' document. Expected one Run only.") - if name == "stop": - self._complete = doc["run_start"] - self._collector.append((name, doc)) - - def retrieve(self): - """ - Return a BlueskyRun. If one is not ready, return None. - """ - if not self._complete: - return None - if self._run is None: - - def gen_func(): - yield from self._collector - - # TODO in a future PR: - # We have to mock up an Entry. - # Can we avoid this after the Entry refactor? - from types import SimpleNamespace - _, start_doc = next(iter(self._collector)) - entry = SimpleNamespace(name=start_doc["uid"]) - - self._run = BlueskyRunFromGenerator( - gen_func, (), {}, get_filler=self._get_filler, - transforms=self._transforms, entry=entry) - return self._run - - def __repr__(self): - # Either <SingleRunCache in progress> - # or <SingleRunCache uid="..."> - if self._complete: - return f"<SingleRunCache uid={self._complete}>" - else: - return "<SingleRunCache in progress>" - - -class BlueskyRunFromGenerator(BlueskyRun): - - def __init__(self, gen_func, gen_args, gen_kwargs, get_filler, - transforms, **kwargs): - - document_cache = DocumentCache() - - for item in gen_func(*gen_args, **gen_kwargs): - document_cache(*item) - - assert document_cache.start_doc is not None - - def get_run_start(): - return document_cache.start_doc - - def get_run_stop(): - return document_cache.stop_doc - - def get_event_descriptors(): - return list(document_cache.descriptors.values()) - - def get_event_pages(descriptor_uid, skip=0, limit=None): - if skip != 0 and limit is not None: - raise NotImplementedError - return document_cache.event_pages[descriptor_uid] - - def get_event_count(descriptor_uid): - return sum(len(page['seq_num']) - for page in (document_cache.event_pages[descriptor_uid])) - - def get_resource(uid): - return document_cache.resources[uid] - - def get_resources(): - return list(document_cache.resources.values()) - - def lookup_resource_for_datum(datum_id): - return document_cache.resource_uid_by_datum_id[datum_id] - - def get_datum_pages(resource_uid, skip=0, limit=None): - if skip != 0 and limit is not None: - raise NotImplementedError - return document_cache.datum_pages_by_resource[resource_uid] - - super().__init__( - get_run_start=get_run_start, - get_run_stop=get_run_stop, - get_event_descriptors=get_event_descriptors, - get_event_pages=get_event_pages, - get_event_count=get_event_count, - get_resource=get_resource, - get_resources=get_resources, - lookup_resource_for_datum=lookup_resource_for_datum, - get_datum_pages=get_datum_pages, - get_filler=get_filler, - transforms=transforms, - **kwargs) - - -def _transpose(in_data, keys, field): - """Turn a list of dicts into dict of lists - - Parameters - ---------- - in_data : list - A list of dicts which contain at least one dict. - All of the inner dicts must have at least the keys - in `keys` - - keys : list - The list of keys to extract - - field : str - The field in the outer dict to use - - Returns - ------- - transpose : dict - The transpose of the data - """ - out = {k: [None] * len(in_data) for k in keys} - for j, ev in enumerate(in_data): - dd = ev[field] - for k in keys: - out[k][j] = dd[k] - for k in keys: - try: - # compatibility with dask < 2 - if hasattr(out[k][0], 'shape'): - out[k] = dask.array.stack(out[k]) - else: - out[k] = dask.array.array(out[k]) - except NotImplementedError: - # There are data structured that dask auto-chunking cannot handle, - # such as an list of list of variable length. For now, let these go - # out as plain numpy arrays. In the future we might make them dask - # arrays with manual chunks. - out[k] = numpy.asarray(out[k]) - except ValueError as err: - # TEMPORARY EMERGENCY FALLBACK - # If environment variable is set to anything but 0, work around - # dask and return a numpy array. - databroker_array_fallback = os.environ.get('DATABROKER_ARRAY_FALLBACK') - if databroker_array_fallback != "0": - out[k] = numpy.asarray(out[k]) - warnings.warn( - f"Creating a dask array raised an error. Because the " - f"environment variable DATABROKER_ARRAY_FALLBACK was set " - f"to {databroker_array_fallback} we have caught the error and " - f"fallen back to returning a numpy array instead. This may be " - f"very slow. The underlying issue should be resolved. The " - f"error was {err!r}.") - else: - raise - - return out - - -def _ft(timestamp): - "format timestamp" - if isinstance(timestamp, str): - return timestamp - # Truncate microseconds to miliseconds. Do not bother to round. - return (datetime.fromtimestamp(timestamp) - .strftime('%Y-%m-%d %H:%M:%S.%f'))[:-3] - - -def _xarray_to_event_gen(data_xarr, ts_xarr, page_size): - for start_idx in range(0, len(data_xarr['time']), page_size): - stop_idx = start_idx + page_size - data = {name: variable.values - for name, variable in - data_xarr.isel({'time': slice(start_idx, stop_idx)}).items() - if ':' not in name} - ts = {name: variable.values - for name, variable in - ts_xarr.isel({'time': slice(start_idx, stop_idx)}).items() - if ':' not in name} - event_page = {} - seq_num = data.pop('seq_num') - ts.pop('seq_num') - uids = data.pop('uid') - ts.pop('uid') - event_page['data'] = data - event_page['timestamps'] = ts - event_page['time'] = data_xarr['time'][start_idx:stop_idx].values - event_page['uid'] = uids - event_page['seq_num'] = seq_num - event_page['filled'] = {} - - yield event_page - - -
[docs]def discover_handlers(entrypoint_group_name='databroker.handlers', - skip_failures=True): - """ - Discover handlers via entrypoints. - - Parameters - ---------- - entrypoint_group_name: str - Default is 'databroker.handlers', the "official" databroker entrypoint - for handlers. - skip_failures: boolean - True by default. Errors loading a handler class are converted to - warnings if this is True. - - Returns - ------- - handler_registry: dict - A suitable default handler registry - """ - group = entrypoints.get_group_named(entrypoint_group_name) - group_all = entrypoints.get_group_all(entrypoint_group_name) - if len(group_all) != len(group): - # There are some name collisions. Let's go digging for them. - for name, matches in itertools.groupby(group_all, lambda ep: ep.name): - matches = list(matches) - if len(matches) != 1: - winner = group[name] - warnings.warn( - f"There are {len(matches)} entrypoints for the " - f"databroker handler spec {name!r}. " - f"They are {matches}. The match {winner} has won the race.") - handler_registry = {} - for name, entrypoint in group.items(): - try: - handler_class = entrypoint.load() - except Exception as exc: - if skip_failures: - warnings.warn(f"Skipping {entrypoint!r} which failed to load. " - f"Exception: {exc!r}") - continue - else: - raise - handler_registry[name] = handler_class - - return handler_registry
- - -
[docs]def parse_handler_registry(handler_registry): - """ - Parse mapping of spec name to 'import path' into mapping to class itself. - - Parameters - ---------- - handler_registry : dict - Values may be string 'import paths' to classes or actual classes. - - Examples - -------- - Pass in name; get back actual class. - - >>> parse_handler_registry({'my_spec': 'package.module.ClassName'}) - {'my_spec': <package.module.ClassName>} - - """ - result = {} - for spec, handler_str in handler_registry.items(): - if isinstance(handler_str, str): - module_name, _, class_name = handler_str.rpartition('.') - class_ = getattr(importlib.import_module(module_name), class_name) - else: - class_ = handler_str - result[spec] = class_ - return result
- - -
[docs]def parse_transforms(transforms): - """ - Parse mapping of spec name to 'import path' into mapping to class itself. - - Parameters - ---------- - transforms : collections.abc.Mapping or None - A collections.abc.Mapping or subclass, that maps any subset of the - keys {start, stop, resource, descriptor} to a function (or a string - import path) that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest (e.g via a - database migration). - - Examples - -------- - Pass in name; get back actual class. - - >>> parse_transforms({'descriptor': 'package.module.function_name'}) - {'descriptor': <package.module.function_name>} - - """ - transformable = {'start', 'stop', 'resource', 'descriptor'} - - if transforms is None: - result = {key: _no_op for key in transformable} - return result - elif isinstance(transforms, collections.abc.Mapping): - if len(transforms.keys() - transformable) > 0: - raise NotImplementedError(f"Transforms for {transforms.keys() - transformable} " - f"are not supported.") - result = {} - - for name in transformable: - transform = transforms.get(name) - if isinstance(transform, str): - module_name, _, class_name = transform.rpartition('.') - function = getattr(importlib.import_module(module_name), class_name) - elif transform is None: - function = _no_op - else: - function = transform - result[name] = function - return result - else: - raise ValueError(f"Invalid transforms argument {transforms}. " - f"transforms must be None or a dictionary.")
- - -# This determines the type of the class that you get on the -# client side. -intake.container.register_container('bluesky-run', RemoteBlueskyRun) -intake.container.register_container( - 'bluesky-event-stream', RemoteBlueskyEventStream) - - -def _concat_dataarray_pages(dataarray_pages): - """ - Combines a iterable of dataarray_pages to a single dataarray_page. - - Parameters - ---------- - dataarray_pages: Iterabile - An iterable of event_pages with xarray.dataArrays in the data, - timestamp, and filled fields. - Returns - ------ - event_page : dict - A single event_pages with xarray.dataArrays in the data, - timestamp, and filled fields. - """ - pages = list(dataarray_pages) - if len(pages) == 1: - return pages[0] - - array_keys = ['seq_num', 'time', 'uid'] - data_keys = dataarray_pages[0]['data'].keys() - - return {'descriptor': pages[0]['descriptor'], - **{key: list(itertools.chain.from_iterable( - [page[key] for page in pages])) for key in array_keys}, - 'data': {key: xarray.concat([page['data'][key] for page in pages], - dim='concat_dim') - for key in data_keys}, - 'timestamps': {key: xarray.concat([page['timestamps'][key] - for page in pages], dim='concat_dim') - for key in data_keys}, - 'filled': {key: xarray.concat([page['filled'][key] - for page in pages], dim='concat_dim') - for key in data_keys}} - - -def _event_page_to_dataarray_page(event_page, dims=None, coords=None): - """ - Converts the event_page's data, timestamps, and filled to xarray.DataArray. - - Parameters - ---------- - event_page: dict - A EventPage document - dims: tuple - Tuple of dimension names associated with the array - coords: dict-like - Dictionary-like container of coordinate arrays - Returns - ------ - event_page : dict - An event_pages with xarray.dataArrays in the data, - timestamp, and filled fields. - """ - if coords is None: - coords = {'time': event_page['time']} - if dims is None: - dims = ('time',) - - array_keys = ['seq_num', 'time', 'uid'] - data_keys = event_page['data'].keys() - - return {'descriptor': event_page['descriptor'], - **{key: event_page[key] for key in array_keys}, - 'data': {key: xarray.DataArray( - event_page['data'][key], dims=dims, coords=coords, name=key) - for key in data_keys}, - 'timestamps': {key: xarray.DataArray( - event_page['timestamps'][key], dims=dims, coords=coords, name=key) - for key in data_keys}, - 'filled': {key: xarray.DataArray( - event_page['filled'][key], dims=dims, coords=coords, name=key) - for key in data_keys}} - - -def _dataarray_page_to_dataset_page(dataarray_page): - - """ - Converts the dataarray_page's data, timestamps, and filled to xarray.DataSet. - - Parameters - ---------- - dataarray_page: dict - Returns - ------ - dataset_page : dict - """ - array_keys = ['seq_num', 'time', 'uid'] - - return {'descriptor': dataarray_page['descriptor'], - **{key: dataarray_page[key] for key in array_keys}, - 'data': xarray.merge(dataarray_page['data'].values()), - 'timestamps': xarray.merge(dataarray_page['timestamps'].values()), - 'filled': xarray.merge(dataarray_page['filled'].values())} - - -def coerce_dask(handler_class, filler_state): - # If the handler has its own delayed logic, defer to that. - if hasattr(handler_class, 'return_type'): - if handler_class.return_type['delayed']: - return handler_class - - # Otherwise, provide best-effort dask support by wrapping each datum - # payload in dask.array.from_delayed. This means that each datum will be - # one dask task---it cannot be rechunked into multiple tasks---but that - # may be sufficient for many handlers. - class Subclass(handler_class): - - def __call__(self, *args, **kwargs): - descriptor = filler_state.descriptor - key = filler_state.key - shape = extract_shape(descriptor, key, filler_state.resource) - # there is an un-determined size (-1) in the shape, abandon - # lazy as it will not work - if any(s <= 0 for s in shape): - return dask.array.from_array(super().__call__(*args, **kwargs)) - else: - dtype = extract_dtype(descriptor, key) - load_chunk = dask.delayed(super().__call__)(*args, **kwargs) - return dask.array.from_delayed(load_chunk, shape=shape, dtype=dtype) - - return Subclass - - -# This adds a 'delayed' option to event_model.Filler's `coerce` parameter. -# By adding it via plugin, we avoid adding a dask.array dependency to -# event-model and we keep the fiddly hacks into extract_shape here in -# databroker, a faster-moving and less fundamental library than event-model. -event_model.register_coercion('delayed', coerce_dask) - - -def extract_shape(descriptor, key, resource=None): - """ - Patch up misreported 'shape' metadata in old documents. - - This uses heuristcs to guess if the shape looks wrong and determine the - right one. Once historical data has been fixed, this function will be - reused to:: - - return descriptor['data_keys'][key]['shape'] - """ - data_key = descriptor['data_keys'][key] - if resource is not None: - if "frame_per_point" in resource.get("resource_kwargs", {}): - # This is a strong signal that the correct num_images value is here. - num_images = resource["resource_kwargs"]["frame_per_point"] - else: - # Otherwise try to find something ending in 'num_images' in the - # configuration dict associated with this device. - object_keys = descriptor.get('object_keys', {}) - for object_name, data_keys in object_keys.items(): - if key in data_keys: - break - else: - raise RuntimeError(f"Could not figure out shape of {key}") - for k, v in descriptor['configuration'][object_name]['data'].items(): - if k.endswith('num_images'): - num_images = v - break - else: - num_images = -1 - # Work around bug in https://github.com/bluesky/ophyd/pull/746 - # Broken ophyd reports (x, y, 0). We want (num_images, y, x). - if len(data_key['shape']) == 3 and data_key['shape'][-1] == 0: - x, y, _ = data_key['shape'] - shape = (num_images, y, x) - else: - shape = data_key['shape'] - if num_images == -1: - # Along this path, we have no way to make dask work. The calling code - # will fall back to numpy. - return shape - else: - # Along this path, we have inferred that a -1 in here is num_images, - # extracted based on inspecting the resource or the descriptor, - # and we should replace -1 with that. - shape_ = [] - for item in shape: - if item == -1: - shape_.append(num_images) - else: - shape_.append(item) - return shape_ - - -def extract_dtype(descriptor, key): - """ - Work around the fact that we currently report jsonschema data types. - """ - reported = descriptor['data_keys'][key]['dtype'] - if reported == 'array': - return float # guess! - else: - return reported - - -def _no_op(doc): - return doc - - -# This comes from the old databroker.core from before intake-bluesky was merged -# in. It apparently was useful for back-compat at some point. -from databroker._core import Header # noqa: 401, 402 -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/pims_readers.html b/databroker/_modules/databroker/pims_readers.html deleted file mode 100644 index fd553018b9..0000000000 --- a/databroker/_modules/databroker/pims_readers.html +++ /dev/null @@ -1,253 +0,0 @@ - - - - - - - - - - - - databroker.pims_readers — DataBroker 0.0.6 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -

Source code for databroker.pims_readers

-from .databroker import DataBroker, _MDS_SINGLETON, _FS_SINGLETON
-from .core import Images as _Images
-
-
-
[docs]def get_images(headers, name, handler_registry=None, - handler_override=None): - """ - Load images from a detector for given Header(s). - - Parameters - ---------- - fs: FileStoreRO - headers : Header or list of Headers - name : string - field name (data key) of a detector - handler_registry : dict, optional - mapping spec names (strings) to handlers (callable classes) - handler_override : callable class, optional - overrides registered handlers - - - Example - ------- - >>> header = DataBroker[-1] - >>> images = Images(header, 'my_detector_lightfield') - >>> for image in images: - # do something - """ - res = DataBroker.get_images(headers=headers, name=name, - handler_registry=handler_registry, - handler_override=handler_override) - return res
- - -def Images(headers, name, handler_registry=None, handler_override=None): - """ - Load images from a detector for given Header(s). - - Parameters - ---------- - headers : Header or list of Headers - name : str - field name (data key) of a detector - handler_registry : dict, optional - mapping spec names (strings) to handlers (callable classes) - handler_override : callable class, optional - overrides registered handlers - - Example - ------- - >>> header = DataBroker[-1] - >>> images = Images(header, 'my_detector_lightfield') - >>> for image in images: - # do something - """ - return _Images(_MDS_SINGLETON, _FS_SINGLETON, headers=headers, name=name, - handler_registry=handler_registry, - handler_override=handler_override) -
- -
-
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/utils.html b/databroker/_modules/databroker/utils.html deleted file mode 100644 index 02f54296d0..0000000000 --- a/databroker/_modules/databroker/utils.html +++ /dev/null @@ -1,655 +0,0 @@ - - - - - - - - - - - databroker.utils — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker.utils
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker.utils

-from datetime import datetime
-import collections
-import doct
-import glob
-import numpy as np
-import os
-import pytz
-import sys
-import threading
-import warnings
-import yaml
-
-
-class ALL:
-    "Sentinel used as the default value for stream_name"
-    pass
-
-
-def ensure_path_exists(path, exist_ok=True):
-    return os.makedirs(path, exist_ok=exist_ok)
-
-
-def sanitize_np(val):
-    "Convert any numpy objects into built-in Python types."
-    if isinstance(val, (np.generic, np.ndarray)):
-        if np.isscalar(val):
-            return val.item()
-        return val.tolist()
-    return val
-
-
-def apply_to_dict_recursively(d, f):
-    for key, val in d.items():
-        if hasattr(val, 'items'):
-            d[key] = apply_to_dict_recursively(val, f)
-        d[key] = f(val)
-
-
-def format_time(search_dict, tz):
-    """Helper function to format the time arguments in a search dict
-
-    Expects 'since' and 'until'
-
-    ..warning: Does in-place mutation of the search_dict
-    """
-    # The old names of 'since' and 'until' are 'start_time' and 'stop_time'.
-    if 'since' in search_dict and 'start_time' in search_dict:
-        raise TypeError("cannot use both 'since' and its deprecated name "
-                        "'start_time'")
-    if 'until' in search_dict and 'stop_time' in search_dict:
-        raise TypeError("cannot use both 'until' and its deprecated name "
-                        "'stop_time'")
-    if 'start_time' in search_dict or 'stop_time' in search_dict:
-        warnings.warn("The keyword 'start_time' and 'stop_time' have been "
-                      "renamed to 'since' and 'until'. The old names are "
-                      "deprecated.")
-    time_dict = {}
-    since = search_dict.pop('since', search_dict.pop('start_time', None))
-    until = search_dict.pop('until', search_dict.pop('stop_time', None))
-    if since:
-        time_dict['$gte'] = normalize_human_friendly_time(since, tz)
-    if until:
-        time_dict['$lte'] = normalize_human_friendly_time(until, tz)
-    if time_dict:
-        search_dict['time'] = time_dict
-
-
-# human friendly timestamp formats we'll parse
-_TS_FORMATS = [
-    '%Y-%m-%d %H:%M:%S',
-    '%Y-%m-%d %H:%M',  # these 2 are not as originally doc'd,
-    '%Y-%m-%d %H',     # but match previous pandas behavior
-    '%Y-%m-%d',
-    '%Y-%m',
-    '%Y']
-
-# build a tab indented, '-' bulleted list of supported formats
-# to append to the parsing function docstring below
-_doc_ts_formats = '\n'.join('\t- {}'.format(_) for _ in _TS_FORMATS)
-
-
-def normalize_human_friendly_time(val, tz):
-    """Given one of :
-    - string (in one of the formats below)
-    - datetime (eg. datetime.now()), with or without tzinfo)
-    - timestamp (eg. time.time())
-    return a timestamp (seconds since jan 1 1970 UTC).
-
-    Non string/datetime values are returned unaltered.
-    Leading/trailing whitespace is stripped.
-    Supported formats:
-    {}
-    """
-    # {} is placeholder for formats; filled in after def...
-
-    zone = pytz.timezone(tz)  # tz as datetime.tzinfo object
-    epoch = pytz.UTC.localize(datetime(1970, 1, 1))
-    check = True
-
-    if isinstance(val, str):
-        # unix 'date' cmd format '%a %b %d %H:%M:%S %Z %Y' works but
-        # doesn't get TZ?
-
-        # Could cleanup input a bit? remove leading/trailing [ :,-]?
-        # Yes, leading/trailing whitespace to match pandas behavior...
-        # Actually, pandas doesn't ignore trailing space, it assumes
-        # the *current* month/day if they're missing and there's
-        # trailing space, or the month is a single, non zero-padded digit.?!
-        val = val.strip()
-
-        for fmt in _TS_FORMATS:
-            try:
-                ts = datetime.strptime(val, fmt)
-                break
-            except ValueError:
-                pass
-
-        try:
-            if isinstance(ts, datetime):
-                val = ts
-                check = False
-            else:
-                # what else could the type be here?
-                raise TypeError('expected datetime,'
-                                ' got {:r}'.format(ts))
-
-        except NameError:
-            raise ValueError('failed to parse time: ' + repr(val))
-
-    if check and not isinstance(val, datetime):
-        return val
-
-    if val.tzinfo is None:
-        # is_dst=None raises NonExistent and Ambiguous TimeErrors
-        # when appropriate, same as pandas
-        val = zone.localize(val, is_dst=None)
-
-    return (val - epoch).total_seconds()
-
-
-# fill in the placeholder we left in the previous docstring
-normalize_human_friendly_time.__doc__ = (
-    normalize_human_friendly_time.__doc__.format(_doc_ts_formats)
-)
-
-
-def get_fields(header, name=None):
-    """
-    Return the set of all field names (a.k.a "data keys") in a header.
-
-    Parameters
-    ----------
-    header : Header
-    name : string, optional
-        Get field from only one "event stream" with this name. If None
-        (default) get fields from all event streams.
-
-    Returns
-    -------
-    fields : set
-    """
-    fields = set()
-    for descriptor in header['descriptors']:
-        if name is not None and name != descriptor.get('name', 'primary'):
-            continue
-        for field in descriptor['data_keys'].keys():
-            fields.add(field)
-    return fields
-
-
-DOCT_NAMES = {'resource': 'Resource',
-              'datum': 'Datum',
-              'descriptor': 'Event Descriptor',
-              'event': 'Event',
-              'start': 'Run Start',
-              'stop': 'Run Stop'}
-
-
-
[docs]def wrap_in_doct(name, doc): - """ - Put document contents into a doct.Document object. - - A ``doct.Document`` is a subclass of dict that: - - * is immutable - * provides human-readable :meth:`__repr__` and :meth:`__str__` - * supports dot access (:meth:`__getattr__`) as a synonym for item access - (:meth:`__getitem__`) whenever possible - """ - return doct.Document(DOCT_NAMES[name], doc)
- - -_STANDARD_DICT_ATTRS = dir(dict) - - -class DeprecatedDoct(doct.Document): - "Subclass of doct.Document that warns that dot access may be removed." - # We must use __getattribute__ here, not the gentle __getattr__, in order - # to successfully override doct.Document. doct.Document aggressively edits - # its own __dict__, a subclass's __getattr__ would never be called. - def __getattribute__(self, key): - # Get the result first and let any errors be raised. - res = super(DeprecatedDoct, self).__getattribute__(key) - # Now warn before returning it. - if not (key in _STANDARD_DICT_ATTRS or key.startswith('_')): - # This is not a standard dict attribute. - # Warn that dot access is deprecated. - warnings.warn("Dot access may be removed in a future version. " - "Use ['{0}'] instead of .{0}".format(key)) - if key == '_name': - warnings.warn("In a future version of databroker, plain dicts " - "without a '_name' attribute may be returned. " - "Do not rely on '_name'.") - return res - - -
[docs]def wrap_in_deprecated_doct(name, doc): - """ - Put document contents into a DeprecatedDoct object. - - See :func:`wrap_in_doct`. The difference between :class:`DeprecatedDoct` - and :class:`doct.Document` is a warning that dot access - (:meth:`__getattr__` as a synonym for :meth:`__getitem__`) may be removed - in the future. - """ - return DeprecatedDoct(DOCT_NAMES[name], doc)
- - -class DuplicateHandler(RuntimeError): - pass - - -# Search order is (for unix): -# ~/.config/databroker -# <sys.executable directory>/../etc/databroker -# /etc/databroker -# And for Windows we only look in: -# %APPDATA%/databroker - -if os.name == 'nt': - _user_conf = os.path.join(os.environ['APPDATA'], 'databroker') - CONFIG_SEARCH_PATH = (_user_conf,) -else: - _user_conf = os.path.join(os.path.expanduser('~'), '.config', 'databroker') - _local_etc = os.path.join(os.path.dirname(os.path.dirname(sys.executable)), - 'etc', 'databroker') - _system_etc = os.path.join('/', 'etc', 'databroker') - CONFIG_SEARCH_PATH = (_user_conf, _local_etc, _system_etc) - -SPECIAL_NAME = '_legacy_config' -if 'DATABROKER_TEST_MODE' in os.environ: - SPECIAL_NAME = '_test_legacy_config' - - -
[docs]def list_configs(paths=CONFIG_SEARCH_PATH): - """ - List the names of the available configuration files. - - Returns - ------- - names : list - - See Also - -------- - :func:`describe_configs` - """ - name_list = list() - - for path in paths: - files = glob.glob(os.path.join(path, '*.yml')) - name_list.extend([os.path.basename(f)[:-4] for f in files]) - - names = set(name_list) - - if len(names) != len(name_list): - duplicates = [item for item, count - in collections.Counter(name_list).items() if count > 1] - warnings.warn(f"Duplicate configs found: {duplicates}", UserWarning) - - # Do not include _legacy_config. - names.discard(SPECIAL_NAME) - - return sorted(names)
- - -def describe_configs(): - """ - Get the names and descriptions of available configuration files. - - Returns - ------- - configs : dict - map names to descriptions (if available) - - See Also - -------- - :func:`list_configs` - """ - return {name: lookup_config(name).get('description') - for name in list_configs()} - - -
[docs]def lookup_config(name): - """ - Search for a databroker configuration file with a given name. - - For exmaple, the name 'example' will cause the function to search for: - - * ``~/.config/databroker/example.yml`` - * ``{python}/../etc/databroker/example.yml`` - * ``/etc/databroker/example.yml`` - - where ``{python}`` is the location of the current Python binary, as - reported by ``sys.executable``. It will use the first match it finds. - - Parameters - ---------- - name : string - - Returns - ------- - config : dict - """ - if not name.endswith('.yml'): - name += '.yml' - tried = [] - for path in CONFIG_SEARCH_PATH: - filename = os.path.join(path, name) - tried.append(filename) - if os.path.isfile(filename): - with open(filename) as f: - return yaml.load(f, - Loader=getattr(yaml, 'FullLoader', - yaml.Loader) - ) - else: - raise FileNotFoundError("No config file named {!r} could be found in " - "the following locations:\n{}" - "".format(name, '\n'.join(tried)))
- - -def transpose(in_data, keys, field): - """Turn a list of dicts into dict of lists - - Parameters - ---------- - in_data : list - A list of dicts which contain at least one dict. - All of the inner dicts must have at least the keys - in `keys` - - keys : list - The list of keys to extract - - field : str - The field in the outer dict to use - - Returns - ------- - transpose : dict - The transpose of the data - """ - out = {k: [None] * len(in_data) for k in keys} - for j, ev in enumerate(in_data): - dd = ev[field] - for k in keys: - out[k][j] = dd[k] - return out - - -
[docs]def catalog_search_path(): - """ - List directories that will be searched for catalog YAML files. - - This is a convenience wrapper around functions used by intake to determine - its search path. - - Returns - ------- - directories: tuple - """ - from intake.catalog.default import user_data_dir, global_data_dir - return (user_data_dir(), global_data_dir())
- - -# This object should never be directly instantiated by external code. -# It is defined at module scope only so that it is pickleable, but it is for -# the internal use of LazyMap only. -_LazyMapWrapper = collections.namedtuple('_LazyMapWrapper', ('func', )) - - -class LazyMap(collections.abc.Mapping): - __slots__ = ('__mapping', '__lock') - - def __init__(self, *args, **kwargs): - dictionary = dict(*args, **kwargs) - wrap = _LazyMapWrapper - # TODO should be recursive lock? - self.__lock = threading.Lock() - # TODO type validation? - self.__mapping = {k: wrap(v) for k, v in dictionary.items()} - - def __getitem__(self, key): - # TODO per-key locking? - with self.__lock: - v = self.__mapping[key] - if isinstance(v, _LazyMapWrapper): - # TODO handle exceptions? - v = self.__mapping[key] = v.func() - return v - - def __len__(self): - return len(self.__mapping) - - def __iter__(self): - return iter(self.__mapping) - - def __contains__(self, k): - # make sure checking 'in' does not trigger evaluation - return k in self.__mapping - - def add(self, *args, **kwargs): - dictionary = dict(*args, **kwargs) - wrap = _LazyMapWrapper - with self.__lock: - intersection = set(dictionary).intersection(self.__mapping) - if intersection: - raise TypeError(f"Cannot change the value of existing " - f"keys in a LazyMap. " - f"keys: {intersection} already exists.") - self.__mapping.update({k: wrap(v) for k, v in dictionary.items()}) - - def __getstate__(self): - return self.__mapping - - def __setstate__(self, mapping): - self.__mapping = mapping - self.__lock = threading.Lock() -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/v1.html b/databroker/_modules/databroker/v1.html deleted file mode 100644 index 7cd4ef7900..0000000000 --- a/databroker/_modules/databroker/v1.html +++ /dev/null @@ -1,1976 +0,0 @@ - - - - - - - - - - - databroker.v1 — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker.v1
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker.v1

-from collections.abc import Iterable
-from collections import defaultdict
-from datetime import datetime
-import pandas
-import re
-import warnings
-import time
-import humanize
-import jinja2
-import os
-import shutil
-from types import SimpleNamespace
-import tzlocal
-import xarray
-import event_model
-import intake
-import pymongo
-# Toolz and CyToolz have identical APIs -- same test suite, docstrings.
-try:
-    from cytoolz.dicttoolz import merge
-except ImportError:
-    from toolz.dicttoolz import merge
-
-from .utils import (ALL, format_time, get_fields, wrap_in_deprecated_doct,
-                    ensure_path_exists, lookup_config,
-                    transpose)
-
-
-# The v2 API is expected to grow more options for filled than just True/False
-# (e.g. 'delayed') so it expects a string instead of a boolean.
-_FILL = {True: 'yes', False: 'no'}
-
-
-
[docs]def temp_config(): - raise NotImplementedError("Use temp() instead, which returns a v1.Broker.")
- - -
[docs]def temp(): - from .v2 import temp - catalog = temp() - return Broker(catalog)
- - -class Registry: - """ - An accessor that serves as a backward-compatible shim for Broker.reg - """ - def __init__(self, catalog): - self._catalog = catalog - - @property - def handler_reg(self): - return self._catalog.handler_registry - - @property - def root_map(self): - return self._catalog.root_map - - def register_handler(self, key, handler, overwrite=False): - return self._catalog.register_handler( - key, handler, overwrite=overwrite) - - def deregister_handler(self, key): - return self._catalog.deregister_handler(key) - - def copy_files(self, resource, new_root, - verify=False, file_rename_hook=None, - run_start_uid=None): - """ - Copy files associated with a resource to a new directory. - - The registered handler must have a `get_file_list` method and the - process running this method must have read/write access to both the - source and destination file systems. - - This method does *not* update the assets dataregistry_template. - - Internally the resource level directory information is stored - as two parts: the root and the resource_path. The 'root' is - the non-semantic component (typically a mount point) and the - 'resource_path' is the 'semantic' part of the file path. For - example, it is common to collect data into paths that look like - ``/mnt/DATA/2016/04/28``. In this case we could split this as - ``/mnt/DATA`` as the 'root' and ``2016/04/28`` as the resource_path. - - Parameters - ---------- - resource : Document - The resource to move the files of - - new_root : str - The new 'root' to copy the files into - - verify : bool, optional (False) - Verify that the move happened correctly. This currently - is not implemented and will raise if ``verify == True``. - - file_rename_hook : callable, optional - If provided, must be a callable with signature :: - - def hook(file_counter, total_number, old_name, new_name): - pass - - This will be run in the inner loop of the file copy step and is - run inside of an unconditional try/except block. - - See Also - -------- - `RegistryMoving.shift_root` - `RegistryMoving.change_root` - """ - if verify: - raise NotImplementedError('Verification is not implemented yet') - - def rename_hook_wrapper(hook): - if hook is None: - def noop(n, total, old_name, new_name): - return - return noop - - def safe_hook(n, total, old_name, new_name): - try: - hook(n, total, old_name, new_name) - except Exception: - pass - return safe_hook - - file_rename_hook = rename_hook_wrapper(file_rename_hook) - - run_start_uid = resource.get('run_start', run_start_uid) - if run_start_uid is None: - raise ValueError( - "If the Resource document has no `run_start` key, the " - "caller must provide run_start_uid.") - file_list = self._catalog[run_start_uid].get_file_list(resource) - - # check that all files share the same root - old_root = resource.get('root') - if not old_root: - warnings.warn("There is no 'root' in this resource which " - "is required to be able to change the root. " - "Please use `fs.shift_root` to move some of " - "the path from the 'resource_path' to the " - "'root'. For now assuming '/' as root") - old_root = os.path.sep - - for f in file_list: - if not f.startswith(old_root): - raise RuntimeError('something is very wrong, the files ' - 'do not all share the same root, ABORT') - - # sort out where new files should go - new_file_list = [os.path.join(new_root, - os.path.relpath(f, old_root)) - for f in file_list] - N = len(new_file_list) - # copy the files to the new location - for n, (fin, fout) in enumerate(zip(file_list, new_file_list)): - # copy files - file_rename_hook(n, N, fin, fout) - ensure_path_exists(os.path.dirname(fout)) - shutil.copy2(fin, fout) - - return zip(file_list, new_file_list) - - -
[docs]class Broker: - """ - This supports the original Broker API but implemented on intake.Catalog. - """ - def __init__(self, catalog, *, serializer=None, - external_fetchers=None): - self._catalog = catalog - self.__serializer = serializer - self.external_fetchers = external_fetchers or {} - self.prepare_hook = wrap_in_deprecated_doct - self.aliases = {} - self.filters = {} - self.v2._Broker__v1 = self - self._reg = Registry(catalog) - - @property - def _serializer(self): - if self.__serializer is None: - # The method _get_serializer is an optional method implememented on - # some Broker subclasses to support the Broker.insert() method, - # which is pending deprecation. - if hasattr(self._catalog, '_get_serializer'): - self.__serializer = self._catalog._get_serializer() - return self.__serializer - - @property - def reg(self): - "Registry of externally-stored data" - return self._reg - - @property - def name(self): - return self._catalog.name - - @property - def v1(self): - "A self-reference. This makes v1.Broker and v2.Broker symmetric." - return self - - @property - def v2(self): - "Accessor to the version 2 API." - return self._catalog - -
[docs] @classmethod - def from_config(cls, config, auto_register=True, name=None): - return from_config( - config=config, auto_register=auto_register, name=name)
- -
[docs] def get_config(self): - """ - Return the v0 config dict this was created from, or None if N/A. - """ - if hasattr(self, '_config'): - return self._config
- -
[docs] @classmethod - def named(cls, name, auto_register=True): - """ - Create a new Broker instance using a configuration file with this name. - - Configuration file search path: - - * ``~/.config/databroker/{name}.yml`` - * ``{python}/../etc/databroker/{name}.yml`` - * ``/etc/databroker/{name}.yml`` - - where ``{python}`` is the location of the current Python binary, as - reported by ``sys.executable``. It will use the first match it finds. - - Special Case: The name ``'temp'`` creates a new, temporary - configuration. Subsequent calls to ``Broker.named('temp')`` will - create separate configurations. Any data saved using this temporary - configuration will not be accessible once the ``Broker`` instance has - been deleted. - - Parameters - ---------- - name : string - auto_register : boolean, optional - By default, automatically register built-in asset handlers (classes - that handle I/O for externally stored data). Set this to ``False`` - to do all registration manually. - - Returns - ------- - db : Broker - """ - if name == 'temp': - return temp() - else: - try: - config = lookup_config(name) - except FileNotFoundError: - # Continue on to the v2 way. - pass - else: - db = cls.from_config(config, auto_register=auto_register, name=name) - return db - catalog = getattr(intake.cat, name) - return Broker(catalog)
- - @property - def fs(self): - warnings.warn("fs is deprecated, use `db.reg` instead", stacklevel=2) - return self.reg - -
[docs] def stream_names_given_header(self): - return list(self._catalog)
- -
[docs] def fetch_external(self, start, stop): - return {k: func(start, stop) for - k, func in self.external_fetchers.items()}
- - def _patch_state(self, catalog): - "Copy references to v1 state." - catalog.v1.aliases = self.aliases - catalog.v1.filters = self.filters - catalog.v1.prepare_hook = self.prepare_hook - -
[docs] def __call__(self, text_search=None, **kwargs): - data_key = kwargs.pop('data_key', None) - tz = tzlocal.get_localzone().zone - if self.filters: - filters = self.filters.copy() - format_time(filters, tz) # mutates in place - catalog = self._catalog.search(filters) - self._patch_state(catalog) - else: - catalog = self._catalog - if text_search: - kwargs.update({'$text': {'$search': text_search}}) - format_time(kwargs, tz) # mutates in place - result_catalog = catalog.search(kwargs) - self._patch_state(result_catalog) - return Results(self, result_catalog, - data_key)
- -
[docs] def __getitem__(self, key): - # If this came from a client, we might be getting '-1'. - if not isinstance(key, str) and isinstance(key, Iterable): - return [self[item] for item in key] - if isinstance(key, slice): - if key.start is not None and key.start > -1: - raise ValueError("slice.start must be negative. You gave me " - "key=%s The offending part is key.start=%s" - % (key, key.start)) - if key.stop is not None and key.stop > 0: - raise ValueError("slice.stop must be <= 0. You gave me key=%s. " - "The offending part is key.stop = %s" - % (key, key.stop)) - if key.start is None: - raise ValueError("slice.start cannot be None because we do not " - "support slicing infinitely into the past; " - "the size of the result is non-deterministic " - "and could become too large.") - return [self[index] - for index in reversed(range(key.start, key.stop or 0, key.step or 1))] - datasource = self._catalog[key] - return Header(datasource)
- - get_fields = staticmethod(get_fields) - -
[docs] def get_documents(self, - headers, stream_name=ALL, fields=None, fill=False, - handler_registry=None): - """ - Get all documents from one or more runs. - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is `ALL` which yields documents for all streams. - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping asset pecs (strings) to handlers (callable classes) - - Yields - ------ - name : str - The name of the kind of document - - doc : dict - The payload, may be RunStart, RunStop, EventDescriptor, or Event. - - Raises - ------ - ValueError if any key in `fields` is not in at least one descriptor - pre header. - """ - if handler_registry is not None: - raise NotImplementedError("The handler_registry must be set when " - "the Broker is initialized, usually specified " - "in a configuration file.") - - headers = _ensure_list(headers) - - no_fields_filter = False - if fields is None: - no_fields_filter = True - fields = [] - fields = set(fields) - - comp_re = _compile_re(fields) - - for header in headers: - uid = header.start['uid'] - descs = header.descriptors - - per_desc_discards = {} - per_desc_extra_data = {} - per_desc_extra_ts = {} - for d in descs: - (all_extra_dk, all_extra_data, - all_extra_ts, discard_fields) = _extract_extra_data( - header.start, header.stop, d, fields, comp_re, - no_fields_filter) - - per_desc_discards[d['uid']] = discard_fields - per_desc_extra_data[d['uid']] = all_extra_data - per_desc_extra_ts[d['uid']] = all_extra_ts - - d = d.copy() - dict.__setitem__(d, 'data_keys', d['data_keys'].copy()) - for k in discard_fields: - del d['data_keys'][k] - d['data_keys'].update(all_extra_dk) - - if not len(d['data_keys']) and not len(all_extra_data): - continue - - def merge_config_into_event(event): - # Mutate event in place, adding in data and timestamps from the - # descriptor's 'configuration' key. - event_data = event['data'] # cache for perf - desc = event['descriptor'] - event_timestamps = event['timestamps'] - event_data.update(per_desc_extra_data[desc]) - event_timestamps.update(per_desc_extra_ts[desc]) - discard_fields = per_desc_discards[desc] - for field in discard_fields: - del event_data[field] - del event_timestamps[field] - - get_documents_router = _GetDocumentsRouter(self.prepare_hook, - merge_config_into_event, - stream_name=stream_name) - for name, doc in self._catalog[uid].documents(fill=_FILL[bool(fill)], - strict_order=True): - yield from get_documents_router(name, doc)
- -
[docs] def get_events(self, - headers, stream_name='primary', fields=None, fill=False, - handler_registry=None): - """ - Get Event documents from one or more runs. - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is 'primary' - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping asset specs (strings) to handlers (callable classes) - - Yields - ------ - event : Event - The event, optionally with non-scalar data filled in - - Raises - ------ - ValueError if any key in `fields` is not in at least one descriptor - pre header. - """ - - if handler_registry is not None: - raise NotImplementedError("The handler_registry must be set when " - "the Broker is initialized, usually specified " - "in a configuration file.") - - for name, doc in self.get_documents(headers, - fields=fields, - stream_name=stream_name, - fill=fill, - handler_registry=handler_registry): - if name == 'event': - yield doc
- -
[docs] def get_table(self, - headers, stream_name='primary', fields=None, fill=False, - handler_registry=None, - convert_times=True, timezone=None, localize_times=True): - """ - Load the data from one or more runs as a table (``pandas.DataFrame``). - - Parameters - ---------- - headers : Header or iterable of Headers - The headers to fetch the events for - - stream_name : str, optional - Get events from only "event stream" with this name. - - Default is 'primary' - - fields : List[str], optional - whitelist of field names of interest; if None, all are returned - - Default is None - - fill : bool or Iterable[str], optional - Which fields to fill. If `True`, fill all - possible fields. - - Each event will have the data filled for the intersection - of it's external keys and the fields requested filled. - - Default is False - - handler_registry : dict, optional - mapping filestore specs (strings) to handlers (callable classes) - - convert_times : bool, optional - Whether to convert times from float (seconds since 1970) to - numpy datetime64, using pandas. True by default. - - timezone : str, optional - e.g., 'US/Eastern'; if None, use metadatastore configuration in - `self.mds.config['timezone']` - - handler_registry : dict, optional - mapping asset specs (strings) to handlers (callable classes) - - localize_times : bool, optional - If the times should be localized to the 'local' time zone. If - True (the default) the time stamps are converted to the localtime - zone (as configure in mds). - - This is problematic for several reasons: - - - apparent gaps or duplicate times around DST transitions - - incompatibility with every other time stamp (which is in UTC) - - however, this makes the dataframe repr look nicer - - This implies convert_times. - - Defaults to True to preserve back-compatibility. - - Returns - ------- - table : pandas.DataFrame - """ - - if handler_registry is not None: - raise NotImplementedError( - "The handler_registry must be set when " - "the Broker is initialized, usually specified " - "in a configuration file.") - - headers = _ensure_list(headers) - # TODO --- Use local time I guess. - # if timezone is None: - # timezone = self.mds.config['timezone'] - - no_fields_filter = False - if fields is None: - no_fields_filter = True - fields = [] - fields = set(fields) - - comp_re = _compile_re(fields) - - dfs = [] - for header in headers: - descs = header.descriptors - start = header.start - stop = header.stop - descs = [desc for desc in descs if desc.get('name') == stream_name] - for descriptor in descs: - (all_extra_dk, all_extra_data, - all_extra_ts, discard_fields) = _extract_extra_data( - start, stop, descriptor, fields, comp_re, no_fields_filter) - - all_events = [ - doc for name, doc in - self.get_documents(header, stream_name=stream_name, fill=fill) - if name == 'event' and - doc['descriptor'] == descriptor['uid']] - seq_nums = [ev['seq_num'] for ev in all_events] - times = [ev['time'] for ev in all_events] - keys = list(descriptor['data_keys']) - data = transpose(all_events, keys, 'data') - # timestamps = transpose(all_events, keys, 'timestamps') - - df = pandas.DataFrame(index=seq_nums) - # if converting to datetime64 (in utc or 'local' tz) - if convert_times or localize_times: - times = pandas.to_datetime(times, unit='s') - # make sure this is a series - times = pandas.Series(times, index=seq_nums) - - # if localizing to 'local' time - if localize_times: - times = (times - .dt.tz_localize('UTC') # first make tz aware - # .dt.tz_convert(timezone) # convert to 'local' - .dt.tz_localize(None) # make naive again - ) - - df['time'] = times - for field, values in data.items(): - if field in discard_fields: - continue - df[field] = values - if list(df.columns) == ['time']: - # no content - continue - - for field, v in all_extra_data.items(): - df[field] = v - - dfs.append(df) - - if dfs: - result = pandas.concat(dfs) - else: - # edge case: no data - result = pandas.DataFrame() - result.index.name = 'seq_num' - return result
- -
[docs] def get_images(self, headers, name, - stream_name='primary', - handler_registry=None,): - """ - This method is deprecated. Use Broker.get_documents instead. - - Load image data from one or more runs into a lazy array-like object. - - Parameters - ---------- - headers : Header or list of Headers - name : string - field name (data key) of a detector - handler_registry : dict, optional - mapping spec names (strings) to handlers (callable classes) - - Examples - -------- - >>> header = db[-1] - >>> images = Images(header, 'my_detector_lightfield') - >>> for image in images: - # do something - """ - # Defer this import so that pims is an optional dependency. - from ._legacy_images import Images - headers = _ensure_list(headers) - datasets = [header.xarray_dask(stream_name=stream_name) - for header in headers] - if handler_registry is not None: - raise NotImplementedError( - "The handler_registry parameter is no longer supported " - "and must be None.") - dataset = xarray.merge(datasets) - data_array = dataset[name] - return Images(data_array=data_array)
- -
[docs] def alias(self, key, **query): - """ - Create an alias for a query. - - Parameters - ---------- - key : string - must be a valid Python identifier - query : - keyword argument comprising a query - - Examples - -------- - Define an alias that searches for headers with purpose='calibration'. - - >>> db.alias('cal', purpose='calibration') - - Use it. - - >>> headers = db.cal # -> db(purpose='calibration') - - Review defined aliases. - - >>> db.aliases - {'cal': {'purpose': 'calibration'}} - """ - if hasattr(self, key) and key not in self.aliases: - raise ValueError("'%s' is not a legal alias." % key) - self.aliases[key] = query
- -
[docs] def dynamic_alias(self, key, func): - """ - Create an alias for a "dynamic" query, a function that returns a query. - - Parameters - ---------- - key : string - must be a valid Python identifier - func : callable - When called with no arguments, must return a dict that is a valid - query. - - Examples - -------- - Define an alias to get headers from the last 24 hours. - - >>> import time - >>> db.dynamic_alias('today', - ... lambda: {'since': time.time() - 24*60*60}) - - Use it. - - >>> headers = db.today - - Define an alias to get headers with the 'user' field in metadata - matches the current logged-in user. - - >>> import getpass - >>> db.dynamic_alias('mine', lambda: {'user': getpass.getuser()}) - - Use it - - >>> headers = db.mine - """ - if hasattr(self, key) and key not in self.aliases: - raise ValueError("'%s' is not a legal alias." % key) - self.aliases[key] = func
- -
[docs] def add_filter(self, **kwargs): - """ - Add query to the list of 'filter' queries. - - Any query passed to ``db.add_filter()`` is stashed and "AND-ed" with - all future queries. - - ``db.add_filter(**kwargs)`` is just a convenient way to spell - ``db.filters.update(**kwargs)``. - - Examples - -------- - Filter all searches to restrict results to a specific user after a - March 2017. - - >>> db.add_filter(user='Dan') - >>> db.add_filter(since='2017-3') - - The following query is equivalent to - ``db(user='Dan', plan_name='scan')``. - - >>> db(plan_name='scan') - - Review current filters. - - >>> db.filters - {'user': 'Dan', 'since': '2017-3'} - - Clear filters. - - >>> db.clear_filters() - - See Also - -------- - :meth:`Broker.clear_filters` - - """ - self.filters.update(**kwargs)
- -
[docs] def clear_filters(self, **kwargs): - """ - Clear all 'filter' queries. - - Filter queries are combined with every given query using '$and', - acting as a filter to restrict the results. - - ``Broker.clear_filters()`` is just a convenient way to spell - ``Broker.filters.clear()``. - - See Also - -------- - :meth:`Broker.add_filter` - """ - self.filters.clear()
- - def __getattr__(self, key): - try: - query = self.aliases[key] - except KeyError: - raise AttributeError(key) - if callable(query): - query = query() - return self(**query) - -
[docs] def restream(self, headers, fields=None, fill=False): - """ - Get all Documents from given run(s). - - This output can be used as a drop-in replacement for the output of the - bluesky Run Engine. - - Parameters - ---------- - headers : Header or iterable of Headers - header or headers to fetch the documents for - fields : list, optional - whitelist of field names of interest; if None, all are returned - fill : bool, optional - Whether externally-stored data should be filled in. Defaults to - False. - - Yields - ------ - name, doc : tuple - string name of the Document type and the Document itself. - Example: ('start', {'time': ..., ...}) - - Examples - -------- - >>> def f(name, doc): - ... # do something - ... - >>> h = db[-1] # most recent header - >>> for name, doc in restream(h): - ... f(name, doc) - - See Also - -------- - :meth:`Broker.process` - """ - for payload in self.get_documents(headers, fields=fields, fill=fill): - yield payload
- - stream = restream # compat - -
[docs] def process(self, headers, func, fields=None, fill=False): - """ - Pass all the documents from one or more runs into a callback. - - This output can be used as a drop-in replacement for the output of the - bluesky Run Engine. - - Parameters - ---------- - headers : Header or iterable of Headers - header or headers to process documents from - func : callable - function with the signature `f(name, doc)` - where `name` is a string and `doc` is a dict - fields : list, optional - whitelist of field names of interest; if None, all are returned - fill : bool, optional - Whether externally-stored data should be filled in. Defaults to - False. - - Examples - -------- - >>> def f(name, doc): - ... # do something - ... - >>> h = db[-1] # most recent header - >>> process(h, f) - - See Also - -------- - :meth:`Broker.restream` - """ - for name, doc in self.get_documents(headers, fields=fields, fill=fill): - func(name, doc)
- -
[docs] def export(self, headers, db, new_root=None, copy_kwargs=None): - """ - Serialize a list of runs. - - If a new_root is passed files associated with the run will be moved to - this new location, and the corresponding resource document will be - updated with the new_root. - - Parameters - ---------- - headers : databroker.header - one or more run headers that are going to be exported - db : databroker.Broker - an instance of databroker.Broker class that will be the target to - export info - new_root : str - optional. root directory of files that are going to - be exported - copy_kwargs : dict or None - passed through to the ``copy_files`` method on Registry; - None by default - - Returns - ------ - file_pairs : list - list of (old_file_path, new_file_path) pairs generated by - ``copy_files`` method on Registry. - """ - if copy_kwargs is None: - copy_kwargs = {} - - if isinstance(headers, Header): - headers = [headers] - - file_pairs = [] - - for header in headers: - for name, doc in self._catalog[header.start['uid']].documents(fill='no'): - if name == 'event_page': - for event in event_model.unpack_event_page(doc): - db.insert('event', event) - elif name == 'resource' and new_root: - copy_kwargs.setdefault('run_start_uid', header.start['uid']) - file_pairs.extend(self.reg.copy_files(doc, new_root, **copy_kwargs)) - new_resource = doc.to_dict() - new_resource['root'] = new_root - db.insert(name, new_resource) - else: - db.insert(name, doc) - return file_pairs
- -
[docs] def export_size(self, headers): - """ - Get the size of files associated with a list of headers. - - Parameters - ---------- - headers : :class:databroker.Header: - one or more headers that are going to be exported - - Returns - ------- - total_size : float - total size of all the files associated with the ``headers`` in Gb - """ - headers = _ensure_list(headers) - total_size = 0 - for header in headers: - run = self._catalog[header.start['uid']] - for name, doc in self._catalog[header.start['uid']].documents(fill='no'): - if name == 'resource': - for filepath in run.get_file_list(doc): - total_size += os.path.getsize(filepath) - - return total_size * 1e-9
- -
[docs] def insert(self, name, doc): - if self._serializer is None: - raise RuntimeError("No Serializer was configured for this.") - warnings.warn( - "The method Broker.insert may be removed in a future release of " - "databroker.", PendingDeprecationWarning) - self._serializer(name, doc) - # Make a reasonable effort to keep the Catalog in sync with new data. - if name == 'stop': - self._catalog.force_reload()
- -
[docs] def fill_event(*args, **kwargs): - raise NotImplementedError("This method is no longer supported. If you " - "need this please contact the developers by " - "opening an issue here: " - "https://github.com/bluesky/databroker/issues/new ")
- -
[docs] def fill_events(*args, **kwargs): - raise NotImplementedError("This method is no longer supported. If you " - "need this please contact the developers by " - "opening an issue here: " - "https://github.com/bluesky/databroker/issues/new ")
- -
[docs] def stats(self): - "Access MongoDB storage statistics for this database." - return self.v2.stats()
- - - - - -class Results: - """ - Iterable object encapsulating a results set of Headers - - Parameters - ---------- - catalog : Catalog - search results - data_key : string or None - Special query parameter that filters results - """ - def __init__(self, broker, catalog, data_key): - self._broker = broker - self._catalog = catalog - self._data_key = data_key - - def __iter__(self): - # TODO Catalog.walk() fails. We should probably support Catalog.items(). - for uid, entry in self._catalog._entries.items(): - header = Header(entry()) - if self._data_key is None: - yield header - else: - # Only include this header in the result if `data_key` is found - # in one of its descriptors' data_keys. - for descriptor in header.descriptors: - if self._data_key in descriptor['data_keys']: - yield header - break - - -def _ensure_list(headers): - try: - headers.items() - except AttributeError: - return headers - else: - return [headers] - - -def _compile_re(fields=[]): - """ - Return a regular expression object based on a list of regular expressions. - - Parameters - ---------- - fields : list, optional - List of regular expressions. If fields is empty returns a general RE. - - Returns - ------- - comp_re : regular expression object - - """ - if len(fields) == 0: - fields = ['.*'] - f = ["(?:" + regex + r")\Z" for regex in fields] - comp_re = re.compile('|'.join(f)) - return comp_re - - -def _extract_extra_data(start, stop, d, fields, comp_re, - no_fields_filter): - def _project_header_data(source_data, source_ts, - selected_fields, comp_re): - """Extract values from a header for merging into events - - Parameters - ---------- - source : dict - selected_fields : set - comp_re : SRE_Pattern - - Returns - ------- - data_keys : dict - data : dict - timestamps : dict - """ - fields = (set(filter(comp_re.match, source_data)) - selected_fields) - data = {k: source_data[k] for k in fields} - timestamps = {k: source_ts[k] for k in fields} - - return {}, data, timestamps - - if fields: - event_fields = set(d['data_keys']) - selected_fields = set(filter(comp_re.match, event_fields)) - discard_fields = event_fields - selected_fields - else: - discard_fields = set() - selected_fields = set(d['data_keys']) - - objs_config = d.get('configuration', {}).values() - config_data = merge(obj_conf['data'] for obj_conf in objs_config) - config_ts = merge(obj_conf['timestamps'] - for obj_conf in objs_config) - all_extra_data = {} - all_extra_ts = {} - all_extra_dk = {} - if not no_fields_filter: - for dt, ts in [(config_data, config_ts), - (start, defaultdict(lambda: start['time'])), - (stop, defaultdict(lambda: stop['time']))]: - # Look in the descriptor, then start, then stop. - l_dk, l_data, l_ts = _project_header_data( - dt, ts, selected_fields, comp_re) - all_extra_data.update(l_data) - all_extra_ts.update(l_ts) - selected_fields.update(l_data) - all_extra_dk.update(l_dk) - - return (all_extra_dk, all_extra_data, all_extra_ts, - discard_fields) - - -_HTML_TEMPLATE = """ -{% macro rtable(doc, cap) -%} -<table> -<caption> {{ cap }} </caption> -{%- for key, value in doc | dictsort recursive -%} - <tr> - <th> {{ key }} </th> - <td> - {%- if value.items -%} - <table> - {{ loop(value | dictsort) }} - </table> - {%- elif value is iterable and value is not string -%} - <table> - {%- set outer_loop = loop -%} - {%- for stuff in value -%} - {%- if stuff.items -%} - {{ outer_loop(stuff | dictsort) }} - {%- else -%} - <tr><td>{{ stuff }}</td></tr> - {%- endif -%} - {%- endfor -%} - </table> - {%- else -%} - {%- if key == 'time' -%} - {{ value | human_time }} - {%- else -%} - {{ value }} - {%- endif -%} - {%- endif -%} - </td> - </tr> -{%- endfor -%} -</table> -{%- endmacro %} - -<table> - <tr> - <td>{{ rtable(document.start, 'Start') }}</td> - </tr - <tr> - <td>{{ rtable(document.stop, 'Stop') }}</td> - </tr> - <tr> - <td> - <table> - <caption>Descriptors</caption> - {%- for d in document.descriptors -%} - <tr> - <td> {{ rtable(d, d.get('name')) }} </td> - </tr> - {%- endfor -%} - </table> - </td> -</tr> -</table> -""" - - -def _pretty_print_time(timestamp): - # timestamp needs to be a float or fromtimestamp() will barf - timestamp = float(timestamp) - dt = datetime.fromtimestamp(timestamp).isoformat() - ago = humanize.naturaltime(time.time() - timestamp) - return '{ago} ({date})'.format(ago=ago, date=dt) - - -class InvalidConfig(Exception): - """Raised when the configuration file is invalid.""" - ... - - -def from_config(config, auto_register=True, name=None): - """ - Build (some version of) a Broker instance from a v0 configuration dict. - - This method accepts v1 config files. - - This can return a ``v0.Broker``, ``v1.Broker``, or ``v2.Broker`` depending - on the contents of ``config``. - - If config contains the key 'api_version', it should be set to a value 0, 1, - 0, or 2. That setting will be respected until there is an error, in which - case a warning will be issued and we will fall back to v0. If no - 'api_version' is explicitly set by the configuration file, version 1 will - be used. - """ - forced_version = config.get('api_version') - if forced_version == 0: - from . import v0 - return v0.Broker.from_config(config, auto_register, name) - try: - catalog = _from_v0_config(config, auto_register, name) - except InvalidConfig: - raise - except Exception as exc: - warnings.warn( - f"Failed to load config. Falling back to v0." - f"Exception was: {exc}") - from . import v0 - return v0.Broker.from_config(config, auto_register, name) - if forced_version == 2: - return catalog - elif forced_version is None or forced_version == 1: - broker = Broker(catalog) - broker._config = config # HACK to support Broker.get_config() - return broker - else: - raise ValueError(f"Cannot handle api_version {forced_version}") - - -def _from_v0_config(config, auto_register, name): - mds_module = config['metadatastore']['module'] - if mds_module != 'databroker.headersource.mongo': - raise NotImplementedError( - f"Unable to handle metadatastore.module {mds_module!r}") - mds_class = config['metadatastore']['class'] - if mds_class not in ('MDS', 'MDSRO'): - raise NotImplementedError( - f"Unable to handle metadatastore.class {mds_class!r}") - - assets_module = config['assets']['module'] - if assets_module != 'databroker.assets.mongo': - raise NotImplementedError( - f"Unable to handle assets.module {assets_module!r}") - assets_class = config['assets']['class'] - if assets_class not in ('Registry', 'RegistryRO'): - raise NotImplementedError( - f"Unable to handle assets.class {assets_class!r}") - - # Get the mongo databases. - metadatastore_db = _get_mongo_database(config['metadatastore']['config']) - asset_registry_db = _get_mongo_database(config['assets']['config']) - - from ._drivers.mongo_normalized import BlueskyMongoCatalog - from .core import discover_handlers - - # Update the handler registry. - handler_registry = {} - if auto_register: - handler_registry.update(discover_handlers()) - # In v0, config-specified handlers are *added* to any default ones. - for spec, contents in config.get('handlers', {}).items(): - dotted_object = '.'.join((contents['module'], contents['class'])) - handler_registry[spec] = dotted_object - - root_map = config.get('root_map') - transforms = config.get('transforms') - - return BlueskyMongoCatalog(metadatastore_db, asset_registry_db, - handler_registry=handler_registry, - root_map=root_map, - name=name, - transforms=transforms) - - -_mongo_clients = {} # cache of pymongo.MongoClient instances - - -def _get_mongo_database(config): - """ - Return a MongoClient.database. Use a cache in order to reuse the - MongoClient. - """ - # Check that config contains either uri, or host/port, but not both. - if {'uri', 'host'} <= set(config) or {'uri', 'port'} <= set(config): - raise InvalidConfig( - "The config file must define either uri, or host/port, but not both.") - - uri = config.get('uri') - database = config['database'] - - # If this statement is True then uri does not exist in the config. - # If the config has username and password, turn it into a uri. - # This is only here for backward compatibility. - if {'mongo_user', 'mongo_pwd', 'host', 'port'} <= set(config): - uri = (f"mongodb://{config['mongo_user']}:{config['mongo_pwd']}@" - "f{config['host']}:{config['port']}/") - - if uri: - try: - client = _mongo_clients[uri] - except KeyError: - client = pymongo.MongoClient(uri) - _mongo_clients[uri] = client - else: - host = config.get('host') - port = config.get('port') - try: - client = _mongo_clients[(host, port)] - except KeyError: - client = pymongo.MongoClient(host, port) - _mongo_clients[(host, port)] = client - - return client[database] - - -class _GetDocumentsRouter: - """ - This is used by Broker.get_documents. - - It employs a pattern similar to event_model.DocumentRouter, but the methods - are generators instead of functions. - """ - def __init__(self, prepare_hook, merge_config_into_event, stream_name): - self.prepare_hook = prepare_hook - self.merge_config_into_event = merge_config_into_event - self.stream_name = stream_name - self._descriptors = set() - - def __call__(self, name, doc): - # Special case when there is no Run Stop doc. - # In v0, we returned an empty dict here. We now think better of it. - if name == 'stop' and doc is None: - doc = {} - for new_name, new_doc in getattr(self, name)(doc): - yield new_name, self.prepare_hook(new_name, new_doc) - - def descriptor(self, doc): - "Cache descriptor uid and pass it through if it is stream of interest." - if self.stream_name is ALL or doc.get('name', 'primary') == self.stream_name: - self._descriptors.add(doc['uid']) - yield 'descriptor', doc - - def event_page(self, doc): - "Unpack into events and pass them to event method for more processing." - if doc['descriptor'] in self._descriptors: - for event in event_model.unpack_event_page(doc): - yield from self.event(event) - - def event(self, doc): - "Apply merge_config_into_event." - if doc['descriptor'] in self._descriptors: - # Mutate event in place, merging in content from other documents - # and discarding fields excluded by the user. - self.merge_config_into_event(doc) - # If the mutation above leaves event['data'] empty, omit it. - if doc['data']: - yield 'event', doc - - def datum_page(self, doc): - "Unpack into datum." - for datum in event_model.unpack_datum_page(doc): - yield 'datum', datum - - def datum(self, doc): - yield 'datum', doc - - def start(self, doc): - yield 'start', doc - - def stop(self, doc): - yield 'stop', doc - - def resource(self, doc): - yield 'resource', doc -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/databroker/v2.html b/databroker/_modules/databroker/v2.html deleted file mode 100644 index 9bf1737296..0000000000 --- a/databroker/_modules/databroker/v2.html +++ /dev/null @@ -1,395 +0,0 @@ - - - - - - - - - - - databroker.v2 — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Module code »
  • - -
  • databroker.v2
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

Source code for databroker.v2

-import event_model
-import importlib
-import tempfile
-
-from .core import parse_handler_registry, discover_handlers, parse_transforms
-from intake.catalog import Catalog
-from event_model import DuplicateHandler
-from functools import partial
-from pathlib import Path
-
-
-
[docs]class Broker(Catalog): - """ - This is a thin wrapper around intake.Catalog. - - It includes an accessor the databroker API version 1. - - Parameters - ---------- - handler_registry: dict, optional - This is passed to the Filler or whatever class is given in the - filler_class parameter below. - - Maps each 'spec' (a string identifying a given type or external - resource) to a handler class. - - A 'handler class' may be any callable with the signature:: - - handler_class(resource_path, root, **resource_kwargs) - - It is expected to return an object, a 'handler instance', which is also - callable and has the following signature:: - - handler_instance(**datum_kwargs) - - As the names 'handler class' and 'handler instance' suggest, this is - typically implemented using a class that implements ``__init__`` and - ``__call__``, with the respective signatures. But in general it may be - any callable-that-returns-a-callable. - root_map: dict, optional - This is passed to Filler or whatever class is given in the filler_class - parameter below. - - str -> str mapping to account for temporarily moved/copied/remounted - files. Any resources which have a ``root`` in ``root_map`` will be - loaded using the mapped ``root``. - filler_class: type - This is Filler by default. It can be a Filler subclass, - ``functools.partial(Filler, ...)``, or any class that provides the same - methods as ``DocumentRouter``. - transforms: dict - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - **kwargs : - Additional keyword arguments are passed through to the base class, - Catalog. - """ - # Work around - # https://github.com/intake/intake/issues/545 - _container = None - - def __init__(self, *, handler_registry=None, root_map=None, - filler_class=event_model.Filler, transforms=None, **kwargs): - - # Work around https://github.com/intake/intake/issues/543 - self.auth = kwargs.pop("auth", None) - - if isinstance(filler_class, str): - module_name, _, class_name = filler_class.rpartition('.') - self._filler_class = getattr(importlib.import_module(module_name), class_name) - else: - self._filler_class = filler_class - self._root_map = root_map or {} - self._transforms = parse_transforms(transforms) - if handler_registry is None: - handler_registry = discover_handlers() - self._handler_registry = parse_handler_registry(handler_registry) - self.handler_registry = event_model.HandlerRegistryView( - self._handler_registry) - - self._get_filler = partial(self._filler_class, - handler_registry=self.handler_registry, - root_map=self._root_map, - inplace=False) - - super().__init__(**kwargs) - # The values in the root_map are allowed to be relative to the catalog - # file in order to facilitate portable archives. Not all catalogs comes - # from catalog files, so relative paths are only allowed in root_map if - # the the root_map originates from an actual file. If not, we raise. - for k, v in list(self._root_map.items()): - if not Path(v).is_absolute(): - catalog_dir = self.metadata.get('catalog_dir') - if not catalog_dir: - raise ValueError( - "Found relative path {v} in root_map. " - "Relative paths are only allowed when the catalog " - "is backed by a YAML file, so that paths can be " - "interpreted relative to the location of that file.") - self._root_map[k] = str(Path(catalog_dir, v)) - - @property - def root_map(self): - # This is mutable, so be advised that users *can* mutate it under us. - # The property just prohibits them from setting it to an entirely - # different dict instance. - return self._root_map - - @property - def v1(self): - "Accessor to the version 1 API." - if not hasattr(self, '_Broker__v1'): - from .v1 import Broker - self.__v1 = Broker(self) - return self.__v1 - - @property - def v2(self): - "A self-reference. This makes v1.Broker and v2.Broker symmetric." - return self - - def register_handler(self, spec, handler, overwrite=False): - if (not overwrite) and (spec in self._handler_registry): - original = self._handler_registry[spec] - if original is handler: - return - raise DuplicateHandler( - f"There is already a handler registered for the spec {spec!r}. " - f"Use overwrite=True to deregister the original.\n" - f"Original: {original}\n" - f"New: {handler}") - self._handler_registry[spec] = handler - - def deregister_handler(self, spec): - self._handler_registry.pop(spec, None) - -
[docs] def items(self): - # TEMP: Patch regression in intake 0.6.0. - for key, value in super().items(): - yield key, value.get()
- - -
[docs]def temp(): - """ - Generate a Catalog backed by a temporary directory of msgpack-encoded files. - """ - from databroker._drivers.msgpack import BlueskyMsgpackCatalog - handler_registry = {} - - from databroker.core import discover_handlers - handler_registry = discover_handlers() - - # The temp databroker is often (but not exclusively) used in the context of - # a demo or tutorial with the simulated devices in ophyd.sim, some of which - # require a handler registered for the spec 'NPY_SEQ'. - # With ophyd >= 1.4.0 this handler will be discovered in the normal way - # via discover_handlers() above. The following special case is here to - # support older versions of ophyd, which do not declare a - # 'databroker.handlers' entrypoint. - if 'NPY_SEQ' not in handler_registry: - try: - import ophyd.sim - except ImportError: - pass - else: - handler_registry['NPY_SEQ'] = ophyd.sim.NumpySeqHandler - - tmp_dir = tempfile.mkdtemp() - tmp_data_dir = Path(tmp_dir) / 'data' - catalog = BlueskyMsgpackCatalog( - f"{tmp_data_dir}/*.msgpack", - name='temp', - handler_registry=handler_registry) - return catalog
-
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_modules/index.html b/databroker/_modules/index.html deleted file mode 100644 index 5b78d4132d..0000000000 --- a/databroker/_modules/index.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - Overview: module code — databroker 1.2.0b1 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • »
  • - -
  • Overview: module code
  • - - -
  • - -
  • - -
- - -
-
- - - -
-
- -
- -
- - - - - - - - - - - \ No newline at end of file diff --git a/databroker/_sources/DBEP/DBEP01.rst.txt b/databroker/_sources/DBEP/DBEP01.rst.txt deleted file mode 100644 index 855c99b93b..0000000000 --- a/databroker/_sources/DBEP/DBEP01.rst.txt +++ /dev/null @@ -1,393 +0,0 @@ -============== - Mobile files -============== - -.. contents:: - :local: - - -Status -====== - -FEPs go through a number of phases in their lifetime: - -- **Partially Implemented**: The FEP is being actively discussed, a sub set has been implemented. - -Branches and Pull requests -========================== - - - https://github.com/NSLS-II/filestore/pull/58 - -Abstract -======== - -This FEP adds the ability for filestore to copy / move files around -the file system and keep track of those changes. - -Detailed description -==================== - -This FEP will provide API to - - - database to keep track of the full history of file locations *implemented* - - make a copy of all data from a resource from one location in the file - system to another and update all relevant entries *implemented* - - - This may be trouble for some usage patterns where multiple - resources point to same file - - - move files from one place to another *implemented* - - delete files *implemented* - - delete resources - - verify data at both file system and Datum level - -Implementation -============== - -General Requirements --------------------- - - - implement Datum-level hashing - - - this should be a new collection which is keyed on DatumID and - contains the hash (sha1 or md5) of the values - - may contain additional statistics, proprieties about datum - - - shape, dtype, (min, max, mean, histogram ?) - - may want to stats as separate transient DB - - - each file spec needs class/handler that will, given a resource, - produce a list of all the files that are needed *partial, need to flesh out handlers* - - implement resource < - > absolute path mapping collection - - - this is transient as it can always be re-generated - - need a way to flag as 'alive' or not - - - implement hashing of files - - maybe implement a chroot, as well as path into Resource *implemented, but not as described* - - - this is so that you can say ``change_root(resource_id, new_root)`` - and then the files along with the folder structure would be moved. - - without doing this we could do something like - ``change_root(resource_id, n_base, new_root)`` where n_base is - how many layers of directory to strip off, but this requires - knowing a fair amount about the actually paths involved in the - - Could also do something like ``change_path(path_mutation_func, - resource_id)`` where ``path_mutation_func`` is a str -> str - mapping function which is general, but is not great in terms of - keeping this a controlled process and puts a big burden on the - user. - - if there are multiple copies of the same file be able to control - which version gets hit - - - this needs to be controllable based on which computer the compute - is running on - - -API proposal ------------- - -Currently Implemented -********************* - -Limited API :: - - def change_root(resource, new_root, remove_origin=True, verify=False): - '''Change the root directory of a given resource - - The registered handler must have a `get_file_list` method and the - process running this method must have read/write access to both the - source and destination file systems. - - - Parameters - ---------- - resource_or_uid : Document or str - The resource to move the files of - - new_root : str - The new 'root' to copy the files into - - remove_origin : bool, optional (True) - If the source files should be removed - - verify : bool, optional (False) - Verify that the move happened correctly. This currently - is not implemented and will raise if ``verify == True``. - ''' - - def shift_root(self, resource_or_uid, shift): - '''Shift directory levels between root and resource_path - - This is useful because the root can be change via `change_root`. - - Parameters - ---------- - resource_or_uid : Document or str - The resource to change the root/resource_path allocation - of absolute path. - - shift : int - The amount to shift the split. Positive numbers move more - levels into the root and negative values move levels into - the resource_path - - ''' - - def insert_resource(self, spec, resource_path, resource_kwargs, root=''): - - - -additional public API *draft*:: - - def get_resources_by_root(root, partial=False): - pass - - - def get_resources_by_path(path, partial=False): - pass - - - def get_resources_by_spec(spec): - pass - - - def get_resource_by_uid(uid): - pass - - -extended schema :: - - resource_update = { - resource: uid, - old: original_resource_doc, - new: updated_serouce_doc, - time: timestamp (posix time), - cmd: str, the command that generated the insertion - cmd_kwargs: dict, the inputs to cmd - } - - resource = { - spec: str, - root: str, - resource_path: str, - resource_kwargs: dict, - uid: str - } - -Full proposal -************* - -New python API :: - - def copy_resource(resource_id, new_root, old_root=None): - """Copy all the files of a resource - - Parameters - ---------- - resource_id : uuid - The unique id of the resource to work on - - new_root : str - The path to the location in the filesystem to cop - the files into. The full existing directory structure - will be replicated on top of the now root - - old_root : str, optional - If there exists more than one copy already, select - which one to use - - """ - - def move_resource(resource_id, old_root, new_root): - """Move all files for a resource to a new location - - - This is the same as copy then delete. Because of the - delete step users must be explicit about source path. - - Parameters - ---------- - resource_id : uuid - The unique id of the resource to work on - - old_root : str - If there exists more than one copy already, select - which one to use - - new_root : str - The path to the location in the filesystem to cop - the files into. The full existing directory structure - will be replicated on top of the now root - - """ - - def remove_resource(resource_id, old_root, force_last=False): - """Delete all files associated with a resource - - Parameters - ---------- - resource_id : uuid - The unique id of the resource to work on - - old_root : str - Which set of files to delete - - force_last : bool, optional - If False, will raise RuntimeError rather than - delete the last copy of the files. - - - """ - - def insert_resource(spec, resource_root, resource_path, resource_kwargs=None): - """ - Parameters - ---------- - - spec : str - spec used to determine what handler to use to open this - resource. - - resource_path, resource_root : str or None - Url to the physical location of this resource - - resource_kwargs : dict, optional - resource_kwargs name/value pairs of additional kwargs to be - passed to the handler to open this resource. - - """ - - def retrieve(eid, root_preference=None) - """ - Given a resource identifier return the data. - - The root_preference allows control over which copy - of the data is used if there is more than one available. - - Parameters - ---------- - eid : str - The resource ID (as stored in MDS) - - root_preference : list, optional - A list of preferred root locations to pull data from in - descending order. - - If None, fall back to configurable default. - - Returns - ------- - data : ndarray - The requested data as a numpy array - """ - - -New DB schema:: - - - class Resource(Document): - """ - - Parameters - ---------- - - spec : str - spec used to determine what handler to use to open this - resource. - - resource_path : str - Url to the physical location of the resource - - resource_kwargs : dict - name/value pairs of additional kwargs to be - passed to the handler to open this resource. - - """ - - spec = StringField(required=True, unique=False) - path = StringField(required=True, unique=False) - kwargs = DictField(required=False) - uid = StringField(required=True, unique=True) - - meta = {'indexes': ['-_id', 'resource_root'], 'db_alias': ALIAS} - - - class ResourceRoots(DynamicDocument): - """ - Many to one mapping between Resource documents and chroot paths. - - The idea is that the absolute path of a file contains two - parts, the root, which is set by details of how the file - system is mounted, and the relative path which is set by some - sort of semantics. For example in the path :: - - /mnt/DATA/2015/05/06/my_data.h5 - - ``/mnt/DATA/`` is the root and ``2015/05/06/my_data.h5`` is - the relative path. - - In the case of a URL this would be :: - - http://data.nsls-ii.bnl.gov/xf11id/2015/05/06/my_data.h5 - - the root would be ``http://data.nsls-ii.bnl.gov/`` and the - relative path would be ``xf11id/2015/05/06/my_data.h5`` - - Parameters - ---------- - root : str - The chroot of the resource. - - resource_uid : str - The uid of the resource this is associated with - - """ - root = StringField(required=True, unique=False) - resource_uid = StringField(required=True, unique=False) - - - class File(Document): - """ - This is 'semi-transient', everything in here can be rebuilt - if needed from Resource, Datum, and their helper code, but - the hash can be used for validation - """ - resource_uid = StringField(required=True, unique=False) - root = StringField(required=True, unique=False) - - uid = StringField(required=True, unique=True) - abs_path = StringField(required=True, unique=True) - sha1_hash = StringField(required=True) - size = FloatField(required=True) - exists = Bool(required=True) - - - class DatumStats(DynamicDocument): - datum_uid = StringField(required=True, unique=True) - sha1_hash = StringField(required=True) - shape = ListField(field=IntField()) - - class CommandJournal(Document): - command = StringField(required=True) - args = ListField() - kwargs = DictField() - success = Bool(required=True) - - -In a departure from our standard design protocol let File have the -'exists' field be updated. Or have a collection which is just a -(resource_uid, root) create/delete journal. Another option is to allow -``remove`` to delete entries from `File` collection. - - -Backward compatibility -====================== - -This will require a DB migration and breaks all of the AD instances that -insert into FS. - -Alternatives -============ - -None yet diff --git a/databroker/_sources/DBEP/DBEP02.rst.txt b/databroker/_sources/DBEP/DBEP02.rst.txt deleted file mode 100644 index f72d4b5f14..0000000000 --- a/databroker/_sources/DBEP/DBEP02.rst.txt +++ /dev/null @@ -1,186 +0,0 @@ -=================================================== - Promote Resource / Datum to first-class documents -=================================================== - -.. contents:: - :local: - - -Status -====== - -**Discussion** - - -Branches and Pull requests -========================== - - - -Abstract -======== - -Currently *Resource* and *Datum* are directly inserted into the -*AssetRegistry* by *ophyd*. This breaks the document abstractions -by making a specific consumer 'special'. - - - -Detailed description -==================== - -An odd asymmetry in how databroker works is that the documents for -*HeaderSource* and *EventSource* are emitted by the *RunEngine* and -can be subscribed to by one or more consumers. Each consumer is -notionally independent, each receive all of the documents, and do not -need to coordinate in any way (or even be aware of one another's -existence). In contrast, the *Resource* and *Datum* documents are -inserted directly into an *AssetRegistry* by the *ophyd* objects. -This breaks the separation we have between the data collection process -/ hardware, the generation of the documents, and the consumers of -those documents and leads to several unfortunate situations: - - - *ohpyd* objects hold an instance of an *AssetRegisty* - - we need to keep track of **which** *AssertRegistry* things were - inserted into - - consumers that want access to the asset documents need to also have - a handle to the database that the objects are inserting into - -The proposed solution is to promote *Resource* and *Datum* documents -to be peer documents with *Start*, *Stop*, *Descriptor* and *Event*. -They will appear in the document stream and be inserted into -*DataBroker* via ``db.insert``. This eliminates the 'special' -side-band communication and brings all consumers back to the same -footing. This will require coordinated changes to *event-model*, -*databroker*, *bluesky*, and *ophyd*. - - - -Implementation -============== - -Currently, *ophyd* is responsible for collecting all of the values for -the *Resource* and *Datum* documents except for the uids. The uids -are generated by calls to ``reg.register_*`` and the datum uids are -subsequently returned to the *RunEngine* via ``obj.read``. The -proposed change is: - - 1. *ophyd* objects would be responsible for generating the full *Resource* - and *Datum* documents and providing them to the *RunEngine* to be - emitted. *ophyd* may provide some helpers to make generating compliant - documents easy. - - a. Similar to the current documents, a *Resource* must be emitted - before any *Datum* that refers to it. A *Datum* can only refer - to a *Resource* that as been emitted after the most recent - *Start* and before the *Stop* for the most recent *Start*. - - b. an identical (including uid) *Resource* and *Datum* maybe - emitted more than once, the consumers will need to handle this. - - c. The *Datum* documents must be yielded only in the first - ``collect_asset_docs`` for which there UID is in ``read``. - - d. The *Resource* documents must only be yielded in the first - ``collect_asset_docs`` which includes a *Datum* that refers to - it. - - - e. Calls to ``read`` and ``collect_asset_docs`` must be - idempotent. - - Identical *Resource* and *Datum* documents are to support a single - *Resource* that may span many runs, such as background images, and - still ensure that with in the scope of a *Start* / *Stop* pair a - consumer will see all of the documents required. - - - - 2. in ``save`` before the *Event* document is emitted the *RunEngine* - will acquire and emit any *AssetRegistry* Documents. - - a. in ``save`` the *RunEngine* knows what objects in the bundle, call - ``collect_asset_docs`` method :: - - def collect_asset_docs(self) -> Iterator[Tuple[str, Dict[str, Any]]]: - ... - - which will yield the ``(name, doc)`` pairs for anything that - was just read. - - b. these documents will be emitted **before** the *Event* - - 3. consumers will now have access to all relevant documents and can - do what ever they want with them (insert into an asset registry, - live processing / display, copy files else where) - -event-model ------------ - - 1. add schema for *Resource* and *Datum* - 2. assert that datum_id must be of the form ``{resource_id}/{N}``. - This is required to support columnar stores where the *Datum* - documents are group by *Resource* id. - - -databroker ----------- - - 1. teach ``insert`` how to deal with the additional documents. - 2. revert API changes to use ``register_*`` which generate the uids. - 3. helper tools for generating *Resource* and *Datum* documents - (maybe in ohpyd?) - -ophyd ------ - - 1. implement new document generation methods on all devices that have - external data. - - -bluesky -------- - - 1. implement above logic in ``RunEngine._save`` - -Backward Compatibility -====================== - -This will break all of the devices that currently use *AssetRegistry*, -however it will not change anything on the retrieve side. The -constraints on the *datum_id* can not be applied retro-actively, but -can be applied to all future data. - -This excludes the option of having IOCs directly insert *Resource* and -*Datum* documents and expose *datum_id* values to the EPICS layer. We -only have one experimental use of this (GeRM caproto IOC). This level -of flexibility is not worth non-uniformity at the document level. If -we want to have the IOC generate all of the values (including the -uids), then they should expose those values to EPICs and the *ophyd* -object will only be responsible for marshaling those values. - -Alternatives -============ - -Eliminate *Resource* and *Datum* as stand alone documents ---------------------------------------------------------- - -An alternative considered was to eliminate the *Resource* and *Datum* -documents all together by merging *Resource* into *Descriptor* and -*Datum* into *Event*. However, this would break several long-standing -design principles: - - - all values in ``ev['data']`` are unstructured (scalar, strings, arrays) - - *Descriptors* are immutable - -In addition to breaking the insert side, this would also be a major -change on the retrieval side and would require maintaining either two -implementations forever or to migrate all existing data. - -This would also require the *ophyd* objects having a way to notify the -``RunEngine`` that it's configuration / resource was stale so that the -*Descriptor* cache could be invalidated. (this is probably a good -idea anyway). - -Despite being superficially simpler, the fallout from this alternative -would be far greater. diff --git a/databroker/_sources/DBEP/index.rst.txt b/databroker/_sources/DBEP/index.rst.txt deleted file mode 100644 index 85a5c0dfb2..0000000000 --- a/databroker/_sources/DBEP/index.rst.txt +++ /dev/null @@ -1,12 +0,0 @@ -.. _DBEP-index: - -################################ -Databroker Enhancement Proposals -################################ - -.. toctree:: - :maxdepth: 1 - :glob: - - template - DBEP* diff --git a/databroker/_sources/DBEP/template.rst.txt b/databroker/_sources/DBEP/template.rst.txt deleted file mode 100644 index 050513e2ac..0000000000 --- a/databroker/_sources/DBEP/template.rst.txt +++ /dev/null @@ -1,72 +0,0 @@ -=============== - DBEP Template -=============== - -.. contents:: - :local: - - -This PEP template is a guideline of the sections that a DBEP should -contain. Extra sections may be added if appropriate, and unnecessary -sections may be noted as such. - -Status -====== - -DBEPs go through a number of phases in their lifetime: - -- **Discussion**: The DBEP is being actively discussed on the mailing - list and it is being improved by its author. The mailing list - discussion of the DBEP should include the DBEP number (DBEPxxx) in the - subject line so they can be easily related to the DBEP. - -- **Progress**: Consensus was reached on the mailing list and - implementation work has begun. - -- **Completed**: The implementation has been merged into master. - -- **Superseded**: This DBEP has been abandoned in favor of another - approach. - -Branches and Pull requests -========================== - -All development branches containing work on this DBEP should be linked to from here. - -All pull requests submitted relating to this DBEP should be linked to -from here. (A DBEP does not need to be implemented in a single pull -request if it makes sense to implement it in discrete phases). - -Abstract -======== - -The abstract should be a short description of what the DBEP will achieve. - -Detailed description -==================== - -This section describes the need for the DBEP. It should describe the -existing problem that it is trying to solve and why this DBEP makes the -situation better. It should include examples of how the new -functionality would be used and perhaps some use cases. - -Implementation -============== - -This section lists the major steps required to implement the DBEP. -Where possible, it should be noted where one step is dependent on -another, and which steps may be optionally omitted. Where it makes -sense, each step should include a link related pull requests as the -implementation progresses. - -Backward compatibility -====================== - -This section describes the ways in which the DBEP breaks backward incompatibility. - -Alternatives -============ - -If there were any alternative solutions to solving the same problem, -they should be discussed here, along with a justification for the -chosen approach. diff --git a/databroker/_sources/_as_gen/databroker.Broker.fill_event.rst.txt b/databroker/_sources/_as_gen/databroker.Broker.fill_event.rst.txt deleted file mode 100644 index 15173ceb0c..0000000000 --- a/databroker/_sources/_as_gen/databroker.Broker.fill_event.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker\.Broker\.fill\_event -=============================== - -.. currentmodule:: databroker - -.. automethod:: Broker.fill_event \ No newline at end of file diff --git a/databroker/_sources/_as_gen/databroker.broker.wrap_in_deprecated_doct.rst.txt b/databroker/_sources/_as_gen/databroker.broker.wrap_in_deprecated_doct.rst.txt deleted file mode 100644 index 2cfb4f5e81..0000000000 --- a/databroker/_sources/_as_gen/databroker.broker.wrap_in_deprecated_doct.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker\.broker\.wrap\_in\_deprecated\_doct -============================================== - -.. currentmodule:: databroker.broker - -.. autofunction:: wrap_in_deprecated_doct \ No newline at end of file diff --git a/databroker/_sources/_as_gen/databroker.broker.wrap_in_doct.rst.txt b/databroker/_sources/_as_gen/databroker.broker.wrap_in_doct.rst.txt deleted file mode 100644 index e9dd75aa91..0000000000 --- a/databroker/_sources/_as_gen/databroker.broker.wrap_in_doct.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker\.broker\.wrap\_in\_doct -================================== - -.. currentmodule:: databroker.broker - -.. autofunction:: wrap_in_doct \ No newline at end of file diff --git a/databroker/_sources/api_changes.rst.txt b/databroker/_sources/api_changes.rst.txt deleted file mode 100644 index 06c4f6b838..0000000000 --- a/databroker/_sources/api_changes.rst.txt +++ /dev/null @@ -1,75 +0,0 @@ -.. _api_changes: - -============= - API changes -============= - -Non-backward compatible API changes - - -v0.8.0 -====== - - -``databroker.core`` -------------------- - -This module is semi-private - -Removed ``process``, ``stream``, and ``restream`` as top-level -functions. The implementation now lives in -`databroker.broker.BrokerES`. These functions knew too much about the -internals of the databroker to remain as separate functions. - -``Header`` ----------- - -Change `Header` from a `doct.Document` subclass to a ``attr`` based -class. A majority of the API has been maintained, however there are -changes in what exceptions are raised when trying to mutate the -instance. - -+----------------+--------------------------+---------------------------------------+ -| Method | Old Exception | New Exception | -+================+==========================+=======================================+ -| ``h.pop`` | `doc.DocumentIsReadOnly` | `AttributeError` | -+----------------+--------------------------+---------------------------------------+ -| ``h.update`` | `doc.DocumentIsReadOnly` | `AttributeError` | -+----------------+--------------------------+---------------------------------------+ -| ``del h.x`` | `doc.DocumentIsReadOnly` | `attr.exceptions.FrozenInstanceError` | -+----------------+--------------------------+---------------------------------------+ -| ``del h['x']`` | `doc.DocumentIsReadOnly` | `TypeError` | -+----------------+--------------------------+---------------------------------------+ -| ``h.x = V`` | `doc.DocumentIsReadOnly` | `attr.exceptions.FrozenInstanceError` | -+----------------+--------------------------+---------------------------------------+ -| ``h['x'] * V`` | `doc.DocumentIsReadOnly` | `TypeError` | -+----------------+--------------------------+---------------------------------------+ - -``Header.from_run_start`` -------------------------- - -Take a `DataBroker` object instead of a `MetadataStore` object. This -is now tacked on the `Header` object. - -Changes to functions in `databroker.core` ------------------------------------------ - -Explicitly passed mds/fs have been removed, instead relying on the -DataBroker instance included in the header. - -Break up internal structure of databroker ------------------------------------------ - -* The core functions that touch events have a new required argument, ``es``. - This does not affect the API of the ``Broker`` object, only the functions in - the ``core`` module. - -Top level insert ----------------- - -The top level `DataBroker` now has an ``insert`` method, use this over ``db.mds.insert``. - -v0.4.2 -====== - - - Change ``name`` -> ``stream_name`` in the signature of `get_table` diff --git a/databroker/_sources/api_changes.txt b/databroker/_sources/api_changes.txt deleted file mode 100644 index 3855b509fe..0000000000 --- a/databroker/_sources/api_changes.txt +++ /dev/null @@ -1,11 +0,0 @@ -.. _api_changes: - -API changes -*********** - -Non-backward compatible API changes - -v0.4.2 ------- - - - Change ``name`` -> ``stream_name`` in the signature of `get_table` diff --git a/databroker/_sources/configuration.txt b/databroker/_sources/configuration.txt deleted file mode 100644 index 15738ac6bb..0000000000 --- a/databroker/_sources/configuration.txt +++ /dev/null @@ -1,101 +0,0 @@ -Configuration -============= - -The databroker provides a user-friendly interface that unifies data from -multiple sources. It requires some initial configuration to locate all these -sources. - -This configuration can be done in Python --- typically using a startup -file in an IPython profile so that it doesn't need to be typed every time. -Alternatively, the configuration can be specified in files or environment -variables. The file- or environment-based approach is less customizable, so by -default we recommend using a Python script. - -Script-Based Configuration --------------------------- - -1. Connect to metadatastore through an ``MDSRO`` ("metadata store read-only") - instance. -2. Connect to filestore through a ``FileStoreRO`` ("file store read-only") - instance. -3. Pass these two to ``Broker``, which provides a user-friendly interface the - information in both of these together. - -.. code-block:: python - - from metadatastore.mds import MDSRO # "metadata store read-only" - from filestore.fs import FileStoreRO # "file store read-only" - from databroker import Broker - - # This an example. You'll need to know your local configuration. - mds = MSDRO({'host': 'localhost', - 'port': 27017, - 'database': 'metadatastore-production-v1', - 'timezone': 'US/Eastern'}) - - # This an example. You'll need to know your local configuration. - fs = FileStoreRO({'host': 'localhost', - 'port': 27017, - 'database': 'filestore-production-v1'}) - - db = Broker(mds, fs) - -Configuration Files or Environment Variables ----------------------------------------------- - -DataBroker looks for configuration in: - -``~/.config/metadatastore/connection.yml`` -``/etc/metadatastore.yml`` - -in that order. Create a file like this in either of those locations: - -.. code-block:: bash - - host: localhost - port: 27017 - database: metadatastore-production-v1 - timezone: US/Eastern - -Configuration can also be provided through the environment variables which, -if set, take precedence over the files. - -.. code-block:: bash - - export MDS_HOST=localhost - export MDS_PORT=27017 - export MDS_DATABASE=metadatastore-production-v1 - export MDS_TIMEZONE=US/Eastern - -Likewise, it looks in - -``~/.config/filestore/connection.yml`` -``/etc/filestore.yml`` - -for a file like: - -.. code-block:: bash - - host: localhost - port: 27017 - database: filestore-production-v1 - -which, likewise, can be overridden by environment variables: - -.. code-block:: bash - - export FS_HOST=localhost - export FS_PORT=27017 - export FS_DATABASE=filestore-production-v1 - -Now connecting is as simple as: - -.. code-block:: python - - from databroker import db - -Under the hood, this locates the configuration, instantiates ``MDSRO`` and -``FileStoreRO`` using those parameters, and then instantiates ``Broker``, as -illustrated in the script-based configuration above. - -If no configuration can be found, this will raise an error. diff --git a/databroker/_sources/data-access-overview.rst.txt b/databroker/_sources/data-access-overview.rst.txt deleted file mode 100644 index f681e79836..0000000000 --- a/databroker/_sources/data-access-overview.rst.txt +++ /dev/null @@ -1,108 +0,0 @@ -******************** -Data Access Overview -******************** - -The bluesky ecosystem provides several modes for accessing data: - -* Access Central DataBroker via a Generic Remote Client --- This includes - Remote Desktop, Jupyter, and SSH. -* Portable DataBroker with Local Data --- Let users use ``databroker`` on their - laptops and/or on servers at their home institutions, with all the relevant - data copied locally and no need for a network connection. -* Portable DataBroker with Remote Data --- Let users use ``databroker`` on their - laptops and/or on servers at their home institutions, pulling data from an - HTTP server on demand, and optionally caching it locally. -* Traditional File Export --- Export data to files for existing software that - expects files in a certain format named a certain way. - - -Access Central DataBroker via a Generic Remote Client -===================================================== - -In this mode, users do not install ``databroker`` locally. They use any remote -client---such as Remote Desktop, Jupyter, or SSH---to access a Python -environment on the source machine, and use ``databroker`` there, which -presumably has fast access to the data storage and some compute resources. - - -Portable DataBroker with Local Data -=================================== - -DataBroker is not itself a data store; it is a Python library for accessing -data across a variety of data stores. Therefore, it can be run on a laptop -without network connectivity, accessing data stored in ordinary files or in -a local database. Both are officially supported. - -The process involves: - -#. Identify a subset of the data to be copied locally from the source - institution, given as a query (e.g. a time range) or a list of unique - identifiers. Export the documents into a file-based format (typically - msgpack). Copy any of the large "external" files (e.g. TIFF or HDF5 files - generated by large detectors). -#. Transfer all of this to the target machine, perhaps via ``rsync`` or Globus. - Place a configuration file discoverable by ``databroker`` that points to the - location where the files were transferred. -#. Install the Python library ``databroker`` on the target machine using pip or - conda. - -DataBroker can work on top of a directory of ordinary files just fine; it even -supports the same queries that it would normally run on a database---just less -efficiently. Optionally, ingest the documents into a local database to support -more efficient queries. - -The small utility -`databroker-pack `_ streamlines the -process of "packing" some data from data broker into portable files and -"unpacking" them at their destination. - -Portable DataBroker with Remote Data -==================================== - -In this mode, data copying would happen invisibility to the user and only on -demand. The process involves: - -#. Install the Python library ``databroker`` on the target machine using pip or - conda. -#. Provide databroker with the URL of a remote "remote data catalog" running - that the source facility. - -The user experience from there is exactly the same where the data happens to be -local or remote. Thus, users could write code in one mode and seamless -transition to the other. - -Data is downloaded on demand, and it may be cached locally so that it need not -be repeatedly downloaded. This requires a stable URL and a reliable network -connection. There are *no instances of this mode* known at this time, but all -the software pieces to achieve it exist. It is on the project roadmap. - -Traditional File Export -======================= - -Export the data to files (e.g. TIFFs and/or CSVs) with the metadata of your -choice encoded in filenames. This mode forfeits much of the power of databroker -and the bluesky ecosystem generally, but it is important for supporting -existing workflows and software that expects files in a certain format named a -certain way. - -We expect this mode to become less useful as data sizes increase and scientific -software literacy grows over time. It is a bridge. - -Streaming Export ----------------- - -This means exporting the data during data acquisition such that partial results -are available for reading. The bluesky -`suitcase `_ project provides a pattern -for doing this and ready-to-use implementations for popular formats. - -The streaming export tools may also be used after data acquisition. - -Prompt Export -------------- - -This means exporting the data at the end of data acquisition. (To be precise, -at the end of each "Bluesky Run". The scope of a "Run" is up to the details of -the data acquisition procedure.) This is typically much simpler than streaming -export and can be implemented *ad hoc* by accessing the data from databroker -and writing out a file using the relevant Python I/O library. diff --git a/databroker/_sources/design_manifesto.rst.txt b/databroker/_sources/design_manifesto.rst.txt deleted file mode 100644 index ea77d8d09c..0000000000 --- a/databroker/_sources/design_manifesto.rst.txt +++ /dev/null @@ -1,130 +0,0 @@ -================== - Design Manifesto -================== - -The Nouns -========= - - -``Header`` ----------- - - Attributes - - ``Start`` Document - - ``Stop`` Document - - may want to flip non-existant behavior from ``{}`` -> ``None`` - - Reference back to a ``DataBroker`` - - - properties (ex, LAZY): - - ``streams`` list of streams from all of the event stores - - ``descriptors`` mapping to lists of descriptors per stream - - - Methods: - - ``h.stream(stream_name, fill=True) -> generator`` - - generator for just this stream, - - (name, doc) for all document types - - ``h.table(stream_name, fill=True) -> DataFrame`` - - Dataframe for just this stream - - ``h.events(fill=True) -> generator`` - - all documents as (name, doc) pair - - this should maybe be renamed? - - ``h.es_given_stream(stream_name) -> EventSource`` - - mimic ``doct.Document`` interface? - - -The implementation of these methods should live in the ``Header`` class using the resources -provided by the attached ``DataBroker`` - - -``DataBroker`` --------------- - - Attributes: - - Exactly 1 ``HeaderSource`` - - ``EventSource`` list - - Methods: - - mirror out all search from ``HeaderSource``, converts to ``Header`` objects - - manage filters + search state - - drop everything else! - - insert? - - how to handle ES distribution. - -``HeaderSource`` ----------------- - - databases, not public: - - ``Start`` document collection / table - - ``Stop`` document collection / table - - - Methods - - provides search capabilities over the start / stop documents - - ``hs[uid] -> (Start, Stop)`` - - ``hs[scan_id] -> (Start, Stop)`` - - ``hs[-offset] -> (Start, Stop)`` - - ``hs(full=text, search=method) -> (Start, Stop)`` - -``EventSource`` ---------------- - - databases, not public: - - ``Event`` collection - - ``Descriptor`` document collection - - ``Filestore`` collection(s) / object - - - Methods: - - insert ``es.insert(name, doc)`` - - get streams given a ``Header`` - - ``es.stream_names_given_header(header) -> {stream_names}`` - - - get descriptors - - ``es.descriptors_given_header(header, stream_name=ALL, **kwargs) -> [Descriptor]`` - - - get data payload given a ``Header`` - - ``es.documents_given_header(header, stream_name, fill=False, fields=None, **kwargs) -> doc_generator`` - - ``es.table_given_header(header, stream_name, fill=False, fields=None, **kwargs) -> DataFrame`` - - The reason to keep both the generator and table versions of this - is to allow the event stores to optimize for a given access - pattern. Some data should be stored in columnar / tabular fashion - - - do de-referencing (maybe in place) - - ``es.fill_event(ev, in_place=False, fields=None, handler_registry=None, handler_overrides=None)) -> new_Event`` - - ``es.fill_table(tab, in_place=False, fields=None, handler_registry=None, handler_overrides=None)) -> DataFrame`` - - ``es.fill_event_stream(ev_gen, in_place=False, fields=None, handler_registry=None, handler_overides=None)) -> Event_gen`` - - -Helpers -======= - - - ``stream_to_table(doc_generator) -> DataFrame`` - - ``table_to_stream(table, header=None, stream=None) -> doc_generator`` - - this one may be tricky as going to a table may lose the link back to the run - - particularly if any synthetic columns (ex normalizations) have happened. - - a few accumulator/buffer objects to aid working with sequences of - (name, document) pairs - - - -Random concerns -=============== - - - should implement a global registry of known ``DataBroker`` / - components so that un-pickling a header does not recreate all of - the db connections. We clearly do not have enough meta-classes. - The need for this goes away when we move to a fully service model where the only - state the brokers need to keep is a url and maybe a process-local cache. - - how to not lose metadata back to descriptor / header when going to a table - - should we mutate descriptors when keys are added / removed from - events via filtering / broadcasting - - if we do this, should probably give new uid to descriptor. This - will require doubling down on the idea that for streams of - documents are always mixed types and of the form ``(name, doc)`` - - we may also want to back off on the aggressive de-normalization of - the descriptors at every level. Working always in one process the - cost of de-normalizing is low because we can share an object - (which is the reason that `doct.Document` is immutable), however - if we move to a model where these documents are streamed between - process (local or not) this can result in massive overheads. This - dumps a lot of complexity into the clients, but it is complexity - that we are already having to deal with (because bluesky spits out - uids, DataBroker return the documents in-place.). - - not clear we are not going to end up with two worlds, a document - streaming one and a DataFrame based one. - - there is a possible collision when we merge the config from all of - the objects to do the projection diff --git a/databroker/_sources/fetching.rst.txt b/databroker/_sources/fetching.rst.txt deleted file mode 100644 index 811c6b8b43..0000000000 --- a/databroker/_sources/fetching.rst.txt +++ /dev/null @@ -1,18 +0,0 @@ -.. currentmodule:: databroker.broker - -Fetching Data -============= - -.. note:: - - It helps to understand how data and metadata are organized in our document - model. This is covered well in `this section of the bluesky documentation - `_. This background is not - essential, but we recommend it for more context. - -.. automethod:: Broker.get_table -.. automethod:: Broker.get_images -.. automethod:: Broker.get_events -.. automethod:: Broker.restream -.. automethod:: Broker.process -.. autofunction:: get_fields diff --git a/databroker/_sources/fetching.txt b/databroker/_sources/fetching.txt deleted file mode 100644 index 811c6b8b43..0000000000 --- a/databroker/_sources/fetching.txt +++ /dev/null @@ -1,18 +0,0 @@ -.. currentmodule:: databroker.broker - -Fetching Data -============= - -.. note:: - - It helps to understand how data and metadata are organized in our document - model. This is covered well in `this section of the bluesky documentation - `_. This background is not - essential, but we recommend it for more context. - -.. automethod:: Broker.get_table -.. automethod:: Broker.get_images -.. automethod:: Broker.get_events -.. automethod:: Broker.restream -.. automethod:: Broker.process -.. autofunction:: get_fields diff --git a/databroker/_sources/generated/databroker.Broker.__call__.rst.txt b/databroker/_sources/generated/databroker.Broker.__call__.rst.txt deleted file mode 100644 index 9a2ce86fba..0000000000 --- a/databroker/_sources/generated/databroker.Broker.__call__.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.\_\_call\_\_ -============================== - -.. currentmodule:: databroker - -.. automethod:: Broker.__call__ \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.__getitem__.rst.txt b/databroker/_sources/generated/databroker.Broker.__getitem__.rst.txt deleted file mode 100644 index 24f0a30c71..0000000000 --- a/databroker/_sources/generated/databroker.Broker.__getitem__.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.\_\_getitem\_\_ -================================= - -.. currentmodule:: databroker - -.. automethod:: Broker.__getitem__ \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.add_filter.rst.txt b/databroker/_sources/generated/databroker.Broker.add_filter.rst.txt deleted file mode 100644 index ec94f14d1d..0000000000 --- a/databroker/_sources/generated/databroker.Broker.add_filter.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.add\_filter -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.add_filter \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.alias.rst.txt b/databroker/_sources/generated/databroker.Broker.alias.rst.txt deleted file mode 100644 index 2cb685964e..0000000000 --- a/databroker/_sources/generated/databroker.Broker.alias.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.alias -======================= - -.. currentmodule:: databroker - -.. automethod:: Broker.alias \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.clear_filters.rst.txt b/databroker/_sources/generated/databroker.Broker.clear_filters.rst.txt deleted file mode 100644 index c8e4e246c3..0000000000 --- a/databroker/_sources/generated/databroker.Broker.clear_filters.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.clear\_filters -================================ - -.. currentmodule:: databroker - -.. automethod:: Broker.clear_filters \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.dynamic_alias.rst.txt b/databroker/_sources/generated/databroker.Broker.dynamic_alias.rst.txt deleted file mode 100644 index 62e938e7ef..0000000000 --- a/databroker/_sources/generated/databroker.Broker.dynamic_alias.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.dynamic\_alias -================================ - -.. currentmodule:: databroker - -.. automethod:: Broker.dynamic_alias \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.export.rst.txt b/databroker/_sources/generated/databroker.Broker.export.rst.txt deleted file mode 100644 index fd2de0cb0a..0000000000 --- a/databroker/_sources/generated/databroker.Broker.export.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.export -======================== - -.. currentmodule:: databroker - -.. automethod:: Broker.export \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.export_size.rst.txt b/databroker/_sources/generated/databroker.Broker.export_size.rst.txt deleted file mode 100644 index 156322d374..0000000000 --- a/databroker/_sources/generated/databroker.Broker.export_size.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.export\_size -============================== - -.. currentmodule:: databroker - -.. automethod:: Broker.export_size \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.fetch_external.rst.txt b/databroker/_sources/generated/databroker.Broker.fetch_external.rst.txt deleted file mode 100644 index 4a547dc443..0000000000 --- a/databroker/_sources/generated/databroker.Broker.fetch_external.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.fetch\_external -================================= - -.. currentmodule:: databroker - -.. automethod:: Broker.fetch_external \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.fill_event.rst.txt b/databroker/_sources/generated/databroker.Broker.fill_event.rst.txt deleted file mode 100644 index 20eddadebc..0000000000 --- a/databroker/_sources/generated/databroker.Broker.fill_event.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.fill\_event -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.fill_event \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.fill_events.rst.txt b/databroker/_sources/generated/databroker.Broker.fill_events.rst.txt deleted file mode 100644 index 5181c60390..0000000000 --- a/databroker/_sources/generated/databroker.Broker.fill_events.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.fill\_events -============================== - -.. currentmodule:: databroker - -.. automethod:: Broker.fill_events \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.from_config.rst.txt b/databroker/_sources/generated/databroker.Broker.from_config.rst.txt deleted file mode 100644 index 461776bca3..0000000000 --- a/databroker/_sources/generated/databroker.Broker.from_config.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.from\_config -============================== - -.. currentmodule:: databroker - -.. automethod:: Broker.from_config \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.fs.rst.txt b/databroker/_sources/generated/databroker.Broker.fs.rst.txt deleted file mode 100644 index e48453ebba..0000000000 --- a/databroker/_sources/generated/databroker.Broker.fs.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.fs -==================== - -.. currentmodule:: databroker - -.. autoproperty:: Broker.fs \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_config.rst.txt b/databroker/_sources/generated/databroker.Broker.get_config.rst.txt deleted file mode 100644 index bab8d4e9c7..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_config.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_config -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.get_config \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_documents.rst.txt b/databroker/_sources/generated/databroker.Broker.get_documents.rst.txt deleted file mode 100644 index 1e8d69e87a..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_documents.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_documents -================================ - -.. currentmodule:: databroker - -.. automethod:: Broker.get_documents \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_events.rst.txt b/databroker/_sources/generated/databroker.Broker.get_events.rst.txt deleted file mode 100644 index b21eb9c9a2..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_events.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_events -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.get_events \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_fields.rst.txt b/databroker/_sources/generated/databroker.Broker.get_fields.rst.txt deleted file mode 100644 index 5120c50719..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_fields.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_fields -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.get_fields \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_images.rst.txt b/databroker/_sources/generated/databroker.Broker.get_images.rst.txt deleted file mode 100644 index b4b6f66927..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_images.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_images -============================= - -.. currentmodule:: databroker - -.. automethod:: Broker.get_images \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.get_table.rst.txt b/databroker/_sources/generated/databroker.Broker.get_table.rst.txt deleted file mode 100644 index d3caf841aa..0000000000 --- a/databroker/_sources/generated/databroker.Broker.get_table.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.get\_table -============================ - -.. currentmodule:: databroker - -.. automethod:: Broker.get_table \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.insert.rst.txt b/databroker/_sources/generated/databroker.Broker.insert.rst.txt deleted file mode 100644 index 2f6dc8c219..0000000000 --- a/databroker/_sources/generated/databroker.Broker.insert.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.insert -======================== - -.. currentmodule:: databroker - -.. automethod:: Broker.insert \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.name.rst.txt b/databroker/_sources/generated/databroker.Broker.name.rst.txt deleted file mode 100644 index 6235ffae37..0000000000 --- a/databroker/_sources/generated/databroker.Broker.name.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.name -====================== - -.. currentmodule:: databroker - -.. autoproperty:: Broker.name \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.named.rst.txt b/databroker/_sources/generated/databroker.Broker.named.rst.txt deleted file mode 100644 index 9bceb501b5..0000000000 --- a/databroker/_sources/generated/databroker.Broker.named.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.named -======================= - -.. currentmodule:: databroker - -.. automethod:: Broker.named \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.process.rst.txt b/databroker/_sources/generated/databroker.Broker.process.rst.txt deleted file mode 100644 index d72f5e37b7..0000000000 --- a/databroker/_sources/generated/databroker.Broker.process.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.process -========================= - -.. currentmodule:: databroker - -.. automethod:: Broker.process \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.reg.rst.txt b/databroker/_sources/generated/databroker.Broker.reg.rst.txt deleted file mode 100644 index c9e3ade5f9..0000000000 --- a/databroker/_sources/generated/databroker.Broker.reg.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.reg -===================== - -.. currentmodule:: databroker - -.. autoproperty:: Broker.reg \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.restream.rst.txt b/databroker/_sources/generated/databroker.Broker.restream.rst.txt deleted file mode 100644 index b8fd818bac..0000000000 --- a/databroker/_sources/generated/databroker.Broker.restream.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.restream -========================== - -.. currentmodule:: databroker - -.. automethod:: Broker.restream \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.rst.txt b/databroker/_sources/generated/databroker.Broker.rst.txt deleted file mode 100644 index aef180924c..0000000000 --- a/databroker/_sources/generated/databroker.Broker.rst.txt +++ /dev/null @@ -1,56 +0,0 @@ -databroker.Broker -================= - -.. currentmodule:: databroker - -.. autoclass:: Broker - - - - - .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. - .. autosummary:: - :toctree: - - Broker.__call__ - Broker.add_filter - Broker.alias - Broker.clear_filters - Broker.dynamic_alias - Broker.export - Broker.export_size - Broker.fetch_external - Broker.fill_event - Broker.fill_events - Broker.from_config - Broker.get_config - Broker.get_documents - Broker.get_events - Broker.get_fields - Broker.get_images - Broker.get_table - Broker.insert - Broker.named - Broker.process - Broker.restream - Broker.stats - Broker.stream - Broker.stream_names_given_header - - - - - - - - .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. - .. autosummary:: - :toctree: - - Broker.fs - Broker.name - Broker.reg - Broker.v1 - Broker.v2 - - diff --git a/databroker/_sources/generated/databroker.Broker.stats.rst.txt b/databroker/_sources/generated/databroker.Broker.stats.rst.txt deleted file mode 100644 index 931d30ac27..0000000000 --- a/databroker/_sources/generated/databroker.Broker.stats.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.stats -======================= - -.. currentmodule:: databroker - -.. automethod:: Broker.stats \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.stream.rst.txt b/databroker/_sources/generated/databroker.Broker.stream.rst.txt deleted file mode 100644 index b6549b7e9f..0000000000 --- a/databroker/_sources/generated/databroker.Broker.stream.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.stream -======================== - -.. currentmodule:: databroker - -.. automethod:: Broker.stream \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.stream_names_given_header.rst.txt b/databroker/_sources/generated/databroker.Broker.stream_names_given_header.rst.txt deleted file mode 100644 index eccb09949e..0000000000 --- a/databroker/_sources/generated/databroker.Broker.stream_names_given_header.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.stream\_names\_given\_header -============================================== - -.. currentmodule:: databroker - -.. automethod:: Broker.stream_names_given_header \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.v1.rst.txt b/databroker/_sources/generated/databroker.Broker.v1.rst.txt deleted file mode 100644 index 062448aab3..0000000000 --- a/databroker/_sources/generated/databroker.Broker.v1.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.v1 -==================== - -.. currentmodule:: databroker - -.. autoproperty:: Broker.v1 \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Broker.v2.rst.txt b/databroker/_sources/generated/databroker.Broker.v2.rst.txt deleted file mode 100644 index e21ce86b87..0000000000 --- a/databroker/_sources/generated/databroker.Broker.v2.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Broker.v2 -==================== - -.. currentmodule:: databroker - -.. autoproperty:: Broker.v2 \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.config_data.rst.txt b/databroker/_sources/generated/databroker.Header.config_data.rst.txt deleted file mode 100644 index 7146ff982f..0000000000 --- a/databroker/_sources/generated/databroker.Header.config_data.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.config\_data -============================== - -.. currentmodule:: databroker - -.. automethod:: Header.config_data \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.data.rst.txt b/databroker/_sources/generated/databroker.Header.data.rst.txt deleted file mode 100644 index bcbb296d33..0000000000 --- a/databroker/_sources/generated/databroker.Header.data.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.data -====================== - -.. currentmodule:: databroker - -.. automethod:: Header.data \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.descriptors.rst.txt b/databroker/_sources/generated/databroker.Header.descriptors.rst.txt deleted file mode 100644 index 4a959fbdd7..0000000000 --- a/databroker/_sources/generated/databroker.Header.descriptors.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.descriptors -============================= - -.. currentmodule:: databroker - -.. autoproperty:: Header.descriptors \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.devices.rst.txt b/databroker/_sources/generated/databroker.Header.devices.rst.txt deleted file mode 100644 index d3b48b8691..0000000000 --- a/databroker/_sources/generated/databroker.Header.devices.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.devices -========================= - -.. currentmodule:: databroker - -.. automethod:: Header.devices \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.documents.rst.txt b/databroker/_sources/generated/databroker.Header.documents.rst.txt deleted file mode 100644 index 9023004967..0000000000 --- a/databroker/_sources/generated/databroker.Header.documents.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.documents -=========================== - -.. currentmodule:: databroker - -.. automethod:: Header.documents \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.events.rst.txt b/databroker/_sources/generated/databroker.Header.events.rst.txt deleted file mode 100644 index b28b4fe296..0000000000 --- a/databroker/_sources/generated/databroker.Header.events.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.events -======================== - -.. currentmodule:: databroker - -.. automethod:: Header.events \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.fields.rst.txt b/databroker/_sources/generated/databroker.Header.fields.rst.txt deleted file mode 100644 index 58ce699904..0000000000 --- a/databroker/_sources/generated/databroker.Header.fields.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.fields -======================== - -.. currentmodule:: databroker - -.. automethod:: Header.fields \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.get.rst.txt b/databroker/_sources/generated/databroker.Header.get.rst.txt deleted file mode 100644 index 268602fa79..0000000000 --- a/databroker/_sources/generated/databroker.Header.get.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.get -===================== - -.. currentmodule:: databroker - -.. automethod:: Header.get \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.items.rst.txt b/databroker/_sources/generated/databroker.Header.items.rst.txt deleted file mode 100644 index 2efebc0642..0000000000 --- a/databroker/_sources/generated/databroker.Header.items.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.items -======================= - -.. currentmodule:: databroker - -.. automethod:: Header.items \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.keys.rst.txt b/databroker/_sources/generated/databroker.Header.keys.rst.txt deleted file mode 100644 index 09201c3bf2..0000000000 --- a/databroker/_sources/generated/databroker.Header.keys.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.keys -====================== - -.. currentmodule:: databroker - -.. automethod:: Header.keys \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.rst.txt b/databroker/_sources/generated/databroker.Header.rst.txt deleted file mode 100644 index fb8f6ca026..0000000000 --- a/databroker/_sources/generated/databroker.Header.rst.txt +++ /dev/null @@ -1,46 +0,0 @@ -databroker.Header -================= - -.. currentmodule:: databroker - -.. autoclass:: Header - - - - - .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. - .. autosummary:: - :toctree: - - Header.config_data - Header.data - Header.devices - Header.documents - Header.events - Header.fields - Header.get - Header.items - Header.keys - Header.stream - Header.table - Header.values - Header.xarray - Header.xarray_dask - - - - - - - - .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. - .. autosummary:: - :toctree: - - Header.descriptors - Header.start - Header.stop - Header.stream_names - Header.uid - - diff --git a/databroker/_sources/generated/databroker.Header.start.rst.txt b/databroker/_sources/generated/databroker.Header.start.rst.txt deleted file mode 100644 index cbeb8e524d..0000000000 --- a/databroker/_sources/generated/databroker.Header.start.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.start -======================= - -.. currentmodule:: databroker - -.. autoproperty:: Header.start \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.stop.rst.txt b/databroker/_sources/generated/databroker.Header.stop.rst.txt deleted file mode 100644 index 857b7f21b3..0000000000 --- a/databroker/_sources/generated/databroker.Header.stop.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.stop -====================== - -.. currentmodule:: databroker - -.. autoproperty:: Header.stop \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.stream.rst.txt b/databroker/_sources/generated/databroker.Header.stream.rst.txt deleted file mode 100644 index 1ad68bc7d7..0000000000 --- a/databroker/_sources/generated/databroker.Header.stream.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.stream -======================== - -.. currentmodule:: databroker - -.. automethod:: Header.stream \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.stream_names.rst.txt b/databroker/_sources/generated/databroker.Header.stream_names.rst.txt deleted file mode 100644 index b85de0c799..0000000000 --- a/databroker/_sources/generated/databroker.Header.stream_names.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.stream\_names -=============================== - -.. currentmodule:: databroker - -.. autoproperty:: Header.stream_names \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.table.rst.txt b/databroker/_sources/generated/databroker.Header.table.rst.txt deleted file mode 100644 index 925e914b67..0000000000 --- a/databroker/_sources/generated/databroker.Header.table.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.table -======================= - -.. currentmodule:: databroker - -.. automethod:: Header.table \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.uid.rst.txt b/databroker/_sources/generated/databroker.Header.uid.rst.txt deleted file mode 100644 index 04ddc50951..0000000000 --- a/databroker/_sources/generated/databroker.Header.uid.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.uid -===================== - -.. currentmodule:: databroker - -.. autoproperty:: Header.uid \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.values.rst.txt b/databroker/_sources/generated/databroker.Header.values.rst.txt deleted file mode 100644 index a86166e0b3..0000000000 --- a/databroker/_sources/generated/databroker.Header.values.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.values -======================== - -.. currentmodule:: databroker - -.. automethod:: Header.values \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.xarray.rst.txt b/databroker/_sources/generated/databroker.Header.xarray.rst.txt deleted file mode 100644 index d9efe2f3ef..0000000000 --- a/databroker/_sources/generated/databroker.Header.xarray.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.xarray -======================== - -.. currentmodule:: databroker - -.. automethod:: Header.xarray \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.Header.xarray_dask.rst.txt b/databroker/_sources/generated/databroker.Header.xarray_dask.rst.txt deleted file mode 100644 index f214de0dbb..0000000000 --- a/databroker/_sources/generated/databroker.Header.xarray_dask.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.Header.xarray\_dask -============================== - -.. currentmodule:: databroker - -.. automethod:: Header.xarray_dask \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.databroker.get_events.txt b/databroker/_sources/generated/databroker.databroker.get_events.txt deleted file mode 100644 index dcb040f230..0000000000 --- a/databroker/_sources/generated/databroker.databroker.get_events.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.databroker.get_events -================================ - -.. currentmodule:: databroker.databroker - -.. autodata:: get_events \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.databroker.get_fields.txt b/databroker/_sources/generated/databroker.databroker.get_fields.txt deleted file mode 100644 index 416b6ad858..0000000000 --- a/databroker/_sources/generated/databroker.databroker.get_fields.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.databroker.get_fields -================================ - -.. currentmodule:: databroker.databroker - -.. autofunction:: get_fields \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.databroker.get_table.txt b/databroker/_sources/generated/databroker.databroker.get_table.txt deleted file mode 100644 index 01c5f22cdb..0000000000 --- a/databroker/_sources/generated/databroker.databroker.get_table.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.databroker.get_table -=============================== - -.. currentmodule:: databroker.databroker - -.. autodata:: get_table \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.databroker.process.txt b/databroker/_sources/generated/databroker.databroker.process.txt deleted file mode 100644 index 3061431f66..0000000000 --- a/databroker/_sources/generated/databroker.databroker.process.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.databroker.process -============================= - -.. currentmodule:: databroker.databroker - -.. autodata:: process \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.databroker.restream.txt b/databroker/_sources/generated/databroker.databroker.restream.txt deleted file mode 100644 index b0e5e233fb..0000000000 --- a/databroker/_sources/generated/databroker.databroker.restream.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.databroker.restream -============================== - -.. currentmodule:: databroker.databroker - -.. autodata:: restream \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.list_configs.rst.txt b/databroker/_sources/generated/databroker.list_configs.rst.txt deleted file mode 100644 index 928b760966..0000000000 --- a/databroker/_sources/generated/databroker.list_configs.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.list\_configs -======================== - -.. currentmodule:: databroker - -.. autofunction:: list_configs \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.lookup_config.rst.txt b/databroker/_sources/generated/databroker.lookup_config.rst.txt deleted file mode 100644 index 0caf178be5..0000000000 --- a/databroker/_sources/generated/databroker.lookup_config.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.lookup\_config -========================= - -.. currentmodule:: databroker - -.. autofunction:: lookup_config \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.pims_readers.get_images.txt b/databroker/_sources/generated/databroker.pims_readers.get_images.txt deleted file mode 100644 index 0e6a225383..0000000000 --- a/databroker/_sources/generated/databroker.pims_readers.get_images.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.pims_readers.get_images -================================== - -.. currentmodule:: databroker.pims_readers - -.. autofunction:: get_images \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.temp.rst.txt b/databroker/_sources/generated/databroker.temp.rst.txt deleted file mode 100644 index 365650832f..0000000000 --- a/databroker/_sources/generated/databroker.temp.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.temp -=============== - -.. currentmodule:: databroker - -.. autofunction:: temp \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.temp_config.rst.txt b/databroker/_sources/generated/databroker.temp_config.rst.txt deleted file mode 100644 index 72a5329292..0000000000 --- a/databroker/_sources/generated/databroker.temp_config.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.temp\_config -======================= - -.. currentmodule:: databroker - -.. autofunction:: temp_config \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.wrap_in_deprecated_doct.rst.txt b/databroker/_sources/generated/databroker.wrap_in_deprecated_doct.rst.txt deleted file mode 100644 index e9ec1854a9..0000000000 --- a/databroker/_sources/generated/databroker.wrap_in_deprecated_doct.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.wrap\_in\_deprecated\_doct -===================================== - -.. currentmodule:: databroker - -.. autofunction:: wrap_in_deprecated_doct \ No newline at end of file diff --git a/databroker/_sources/generated/databroker.wrap_in_doct.rst.txt b/databroker/_sources/generated/databroker.wrap_in_doct.rst.txt deleted file mode 100644 index 24fdeef866..0000000000 --- a/databroker/_sources/generated/databroker.wrap_in_doct.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -databroker.wrap\_in\_doct -========================= - -.. currentmodule:: databroker - -.. autofunction:: wrap_in_doct \ No newline at end of file diff --git a/databroker/_sources/headers.rst.txt b/databroker/_sources/headers.rst.txt deleted file mode 100644 index 30ad6b154e..0000000000 --- a/databroker/_sources/headers.rst.txt +++ /dev/null @@ -1,80 +0,0 @@ -How Data and Metadata are Organized -=================================== - -Documents ---------- - -Data and metadata are bundled into what we dub *documents*, Python dictionaries -organized in a `formally specified `_ -way. For every "run" --- loosely speaking, a dataset --- there are four types -of document. - -- A Run Start document, containing all of the metadata known at the start. - Highlights: - - - time --- the start time - - plan_name --- e.g., ``'scan'`` or ``'count'`` - - uid --- randomly-generated ID that uniquely identifies this run - - scan_id --- human-friendly integer scan ID (not necessarily unique) - - any other :doc:`metadata captured execution time ` from the - plan or the user - -- Event documents, containing the actual measurements. Highlights: - - - time --- a timestamp for this group of readings - - data --- a dictionary of readings like - ``{'temperature': 5.0, 'position': 3.0}`` - - timestamps --- a dictionary of individual timestamps for each reading, - from the hardware - -- Event Descriptor documents, with metadata about the measurements in the - events (units, precision, etc.) and about the configuration of the hardware - that generated them. - -- A Run Stop document, containing metadata known only at the end. Highlights: - - - time --- the time when the run was completed - - exit_status --- "success", "abort", or "fail" - -We refer you -`this section of the bluesky documentation `_ -for more details and context. - -Headers -------- - -The result of a :doc:`search ` is a *header*, which bundles together -the metadata-related documents: - -* header.start --- the "Run Start" document -* header.descriptors --- the "Event Descriptor" documents -* header.stop -- the "Run Stop" document - -The only documents omitted from ``header`` are the *events*, which contain -(most of) the actual measured data. That may take more time to load, so we load -it in a separate step. See :doc:`fetching`. - -Some useful examples: - -.. code-block:: python - - # When did this run start and end? - header.start.time - header.stop.time - - # What kind of experimental procedure ("plan") was this? - header.start.plan_name # e.g., 'scan', 'relative_scan', etc. - - # Did it finish successfully? - header.stop.exit_status # 'success', 'fail', or 'abort' - -In later, more specific examples, we'll see more specific and useful metadata. - -.. note:: - - Fields in a header can be accessed in two ways. These are equivalent: - - .. code-block:: python - - header['start']['time'] - header.start.time diff --git a/databroker/_sources/headers.txt b/databroker/_sources/headers.txt deleted file mode 100644 index 30ad6b154e..0000000000 --- a/databroker/_sources/headers.txt +++ /dev/null @@ -1,80 +0,0 @@ -How Data and Metadata are Organized -=================================== - -Documents ---------- - -Data and metadata are bundled into what we dub *documents*, Python dictionaries -organized in a `formally specified `_ -way. For every "run" --- loosely speaking, a dataset --- there are four types -of document. - -- A Run Start document, containing all of the metadata known at the start. - Highlights: - - - time --- the start time - - plan_name --- e.g., ``'scan'`` or ``'count'`` - - uid --- randomly-generated ID that uniquely identifies this run - - scan_id --- human-friendly integer scan ID (not necessarily unique) - - any other :doc:`metadata captured execution time ` from the - plan or the user - -- Event documents, containing the actual measurements. Highlights: - - - time --- a timestamp for this group of readings - - data --- a dictionary of readings like - ``{'temperature': 5.0, 'position': 3.0}`` - - timestamps --- a dictionary of individual timestamps for each reading, - from the hardware - -- Event Descriptor documents, with metadata about the measurements in the - events (units, precision, etc.) and about the configuration of the hardware - that generated them. - -- A Run Stop document, containing metadata known only at the end. Highlights: - - - time --- the time when the run was completed - - exit_status --- "success", "abort", or "fail" - -We refer you -`this section of the bluesky documentation `_ -for more details and context. - -Headers -------- - -The result of a :doc:`search ` is a *header*, which bundles together -the metadata-related documents: - -* header.start --- the "Run Start" document -* header.descriptors --- the "Event Descriptor" documents -* header.stop -- the "Run Stop" document - -The only documents omitted from ``header`` are the *events*, which contain -(most of) the actual measured data. That may take more time to load, so we load -it in a separate step. See :doc:`fetching`. - -Some useful examples: - -.. code-block:: python - - # When did this run start and end? - header.start.time - header.stop.time - - # What kind of experimental procedure ("plan") was this? - header.start.plan_name # e.g., 'scan', 'relative_scan', etc. - - # Did it finish successfully? - header.stop.exit_status # 'success', 'fail', or 'abort' - -In later, more specific examples, we'll see more specific and useful metadata. - -.. note:: - - Fields in a header can be accessed in two ways. These are equivalent: - - .. code-block:: python - - header['start']['time'] - header.start.time diff --git a/databroker/_sources/index.rst.txt b/databroker/_sources/index.rst.txt deleted file mode 100644 index 650d86cdd8..0000000000 --- a/databroker/_sources/index.rst.txt +++ /dev/null @@ -1,40 +0,0 @@ -********** -DataBroker -********** - -Search for data, and retrieve it as SciPy/PyData data structures for -interactive data exploration or in -`a representation suitable for streaming applications `_ . - -If you are here to... - -* use the legacy "Version 1" user interace to databroker, see :doc:`v1/index`; -* try the new "Version 2" user interace to databroker, see the :doc:`v2/user/index`; -* configure or administrate data sources using databroker, see - the :doc:`v2/administrator/index`; -* develop support for new storage formats or understand databroker's internal - workings, see the :doc:`v2/developer/index`. - -.. toctree:: - :maxdepth: 2 - - data-access-overview - overview - installation - v2/index - v1/index - whats_new - DBEP/index - -.. toctree:: - :hidden: - :caption: Bluesky Project - - Homepage - GitHub - -.. toctree:: - :hidden: - :caption: Getting Help - - Gitter diff --git a/databroker/_sources/index.txt b/databroker/_sources/index.txt deleted file mode 100644 index 57cafa1cac..0000000000 --- a/databroker/_sources/index.txt +++ /dev/null @@ -1,22 +0,0 @@ -Data Broker -=========== - -The databroker Python package provides a simple, user-friendly interface -for retrieving stored data and metadata from multiple sources. It loads the -data into memory as built-in Python data types and numpy arrays. - -It is intended to be used in conjunction with the -`bluesky `_ data collection framework, but -it is an independent project that can be used separately if desired. - -Index ------ - -.. toctree:: - :maxdepth: 1 - - configuration - headers - searching - fetching - whats_new diff --git a/databroker/_sources/installation.rst.txt b/databroker/_sources/installation.rst.txt deleted file mode 100644 index 886181760f..0000000000 --- a/databroker/_sources/installation.rst.txt +++ /dev/null @@ -1,53 +0,0 @@ -************ -Installation -************ - -First verify that you have Python 3.6+. - -.. code:: bash - - python3 --version - -If necessary, install it by your method of choice (apt, Homebrew, conda, etc.). - -Facility-Specific Distributions -=============================== - -*We plan for facilities that rely on databroker to provide software -distributions that bundle databroker itself and specific Catalogs for their -users. We will list those distributions here. For users, this will be their -one-stop shop; they need read no further.* - -TO DO - -General Installation -==================== - -This provides a minimal installation that is sufficent for users who just want -to *access* data. - -.. code:: bash - - python3 -m pip install -U databroker - -Administrators and developers may require the optional dependencies as well, -which can be installed by: - -.. code:: bash - - python3 -m pip install .[all] - -Development Installation -======================== - -.. code:: bash - - git clone https://github.com/bluesky/databroker - cd databroker - pip install -e . - -To install all the optional dependencies as well, use: - -.. code:: bash - - pip install -e .[all] diff --git a/databroker/_sources/overview.rst.txt b/databroker/_sources/overview.rst.txt deleted file mode 100644 index 3b0f1df34e..0000000000 --- a/databroker/_sources/overview.rst.txt +++ /dev/null @@ -1,93 +0,0 @@ -****************** -Technical Overview -****************** - -DataBroker provides access to saved data from Bluesky. It does this by applying -Intake to Bluesky. - -`Intake `_ takes the pain out of data access. -It loads data from a growing variety of formats into familiar Scipy/PyData data -structures. - -`Bluesky `_ is a suite of co-developed -Python packages for data acquisition and management designed to drive -experiments and capture data and metadata from experiments and simulations in a -way that interfaces naturally with open-source software in general and the -scientific Python ecosystem in particular. - -.. _transition_plan: - -Transition Plan -=============== - -DataBroker began development in 2015, before Intake. Intake has goals closely -aligned with DataBroker's and a growing community of users from diverse -scientific fields. Starting with DataBroker's 1.0 release, DataBroker is -pivoting to become a distribution of Intake plugins. - -This change will proceed in a way that provides a slow and smooth transition -for existing DataBroker users. - -DataBroker 1.0 --------------- - -The 1.0 release of DataBroker supports multiple usages to enable a smooth -transition for users. - -* The module ``databroker.v0`` provides the databroker code exactly as it was - in previous pre-1.0 releases. This is available as fallback in case of bugs - in v1. -* The module ``databroker.v1`` provides the same user interface as v0, but - built on top of intake instead of databroker's own implementation. With only - narrow exceptions, code that worked on pre-1.0 releases should work on - ``databroker.v1``. This is the "default" interface in DataBroker 1.0; - importing ``from databroker ...`` is an alias for - ``from databroker.v1 ...``. -* The module ``databroker.v2`` is a *very* thin shim around intake. - -The :doc:`v1/index` documentation applies to ``databroker.v0`` and -``databroker.v1``. The :doc:`v2/index` documentation applies to -``databroker.v2``. - -Both ``databroker.v1.Broker`` and ``databroker.v2.Broker`` have accessor -attributes, ``v1`` and ``v2``, that support usage of the other's interface, -making it easy to switch between them. - -Here we make a v1-style Broker but the ``v2`` accessor to try v2-style -features. - -.. code:: python - - from databroker import Broker - db = Broker.named('MY_CATALOG') # a databroker.v1.Broker instance - query = dict(plan_name='count') - db(**query) # v1-style search - db.v1(**query) # a synonym for the above - db.v2.search(query) # v2-style search - -Here we make a v2-style Broker but the ``v1`` accessor to fall back to v1-style -usage. - -.. code:: python - - from databroker import catalog - db = catalog.SOMETHING() # a databroker.v2.Broker instance - query = dict(plan_name='count') - db.search(query) # v2-style search - db.v2.search(query) # a synonym for the above - db.v1(**query) # v1-style search - -Roadmap Beyond 1.0 ------------------- - -* When the v1 implementation is proven reliable, v0 will be removed, greatly - reducing the amount of code in databroker. This should not have an effect on - users, as v1 provides the same interface as v0. The only differences are - internal: v1 uses intake internally instead of its own separate - implementation to achieve the same end. -* If and when the v2 interface is shown to meet users' needs as well or better - than v1, it will become the default. To support existing user code, the v1 - interface will continue to be available in ``databroker.v1``. -* After several years of maintaining both v1 and v2, when all critical user - code has been migrated to v2, v1 may be deprecated and eventually removed. At - that point, DataBroker will be just a distribution of intake plugins. diff --git a/databroker/_sources/release-notes.rst.txt b/databroker/_sources/release-notes.rst.txt deleted file mode 100644 index 1998abf04b..0000000000 --- a/databroker/_sources/release-notes.rst.txt +++ /dev/null @@ -1,11 +0,0 @@ -dev ---- - -* Removed core function ``get_datumkw_by_resuid_gen`` which returned a - generator of datum kwargs. Replaced it with ``get_datum_by_res_gen`` which - returns a generator of complete datum documents. The core function was - exposed through the Registry method ``datum_gen_given_resource`` which, - correspondingly, now returns full datum documents, not just datum kwargs. - -* trying to copy or move files who's resource does not have a 'root' will - intentionally raise. Previously, this would result in an very obscure error. diff --git a/databroker/_sources/searching.rst.txt b/databroker/_sources/searching.rst.txt deleted file mode 100644 index ccb29981da..0000000000 --- a/databroker/_sources/searching.rst.txt +++ /dev/null @@ -1,268 +0,0 @@ -Searching for Data -****************** - -The result of a search is a :ref:`header `, a bundle of metadata about -a given run. In a later section, :doc:`fetching`, we will use the header to -retrieve the data itself. Headers are also useful for quickly reviewing -metadata and generating summaries and logs. - -Search Examples ---------------- - -In these examples, we will collect data using bluesky and then access it -from the databroker. This illustrates how metadata provided at collection time -can be used to search and filter the data during later analysis. - -You do not need to be familiar with bluesky's usage to follow the gist these -examples. For a more detailed understanding, refer to the sections on -`basic usage `_ and -`recording metadata `_. - -By Unique ID -++++++++++++ - -The surest and most direct way to get particular header is to look it up by its -unique ID. This ID is guaranteed to uniquely identify the run forever. - -The RunEngine returns a list of the unique ID(s) when it completes execution. - -.. code-block:: python - - # In all these examples we assume a RunEngine instance, RE, is defined. - # See link to bluesky 'basic usage' documentation above. - - # We also assume that a Broker instance, db, is defined. - # See previous section on configuration. - - uids = RE(some_plan()) - headers = db[uids] - -We could also write down the first 5-6 characters in a uid and use it to look -up the header later. Each unique ID is a randomly-generated string like - -.. code-block:: python - - 'cf24798b-ed6e-4d44-b529-7199fcec41cc' - -but the first 5-6 are virtually always enough to uniquely identify a run. The -databroker accepts a partial uid: - -.. code-block:: python - - db['cf2479'] - -If there is ambiguity (two uids starting with the same couple characters) the -Broker will raise an error. But, again, 5-6 characters are virtually always -sufficient. - -By Plan Name, Detector, or Motor -++++++++++++++++++++++++++++++++ - -Suppose we execute several experiments ("plans", in bluesky jargon) like so. - -.. code-block:: python - - from bluesky.plans import count, scan, relative_scan - from bluesky.examples import motor, det # simulated motor and detector - - RE(count([det])) # 1 - RE(scan([det], motor, -1, 1, 5)) # 2 - RE(relative_scan([det], motor, 1, 10, 10)) # 3 - RE(scan([det], motor, -1, 1, 1000)) # 4 - -We can search by ``plan_name``, which is always automatically recorded in the -metadata. - -.. code-block:: python - - relative_scans = db(plan_name='relative_scan') # 3 - absolute_scans = db(plan_name='scan') # 2 and 4 - -We can also search by ``motors`` or ``detectors``. (All built-in plans provide -this metadata automatically. User-defined plans may or may not provide it.) - -.. code-block:: python - - runs_using_motor = db(motors='motor') # 2, 3, and 4 - runs_using_det = db(detectors='det') # all - -To be precise, ``detectors='det'`` means, "The detector ``det`` is included -in the *list* of detectors used." - -We can also narrow the search by certain plan-specific metadata, like the -number of steps in a scan. - -.. code-block:: python - - long_scan = db(plan_name='scan', num_steps > 50) # 4 - -These may be combined with time-based parameters (presented later below) to -restrict the search to the previous day or week. - -By Custom Metadata Fields -+++++++++++++++++++++++++ - -Again, suppose we execute several plans. This time, we provide some custom -metadata including person operating the equipment and, in some cases, about the -sample and the purpose of each run. - -.. code-block:: python - - from bluesky.plans import count, scan, relative_scan - from bluesky.examples import motor, det # simulated motor and detector - - # This adds {'operator': 'Ken'} to all future runs, unless overridden. - RE.md['operator'] = 'Ken' - - RE(count([det]), purpose='calibration', sample='A') - RE(scan([det], motor, 1, 10, 10), operator='Dan') # temporarily overrides Ken - RE(count([det]), sample='A') # (now back to Ken) - RE(count([det]), sample='B') - - RE.md['operator'] = 'Dan' - - RE(count([det]), purpose='calibration') - RE(scan([det], motor, 1, 10, 10)) - - del RE.md['operator'] # clean up by un-setting operator - -We can search on any of these custom fields. (The words 'operator' and -'purpose' have no special significance to bluesky or databroker --- arbitrary -fields could have been used.) - -.. code-block:: python - - db(sample='A') # return both runs that used sample A - db(purpose='calibration', sample='A') # returns sample A calibration run - db(purpose='calibration') # returns the two calibration runs - db(operator='Dan') # returns three runs by Dan - -Full Text Search ----------------- - -Calling ``db`` with a positional argument like - -.. code-block:: python - - db('calibration') - -performs a full-text search and returns any headers with the value -``'calibration'`` in any field. - -Presently, it searches the full text of Run Start documents, which in the vast -majority of cases contains the metadata one would want to base a search on. In -the future it might be extended to search all fields in the header, depending -on performance considerations. - -Searching by ID or Recency --------------------------- - -With Python's slicing syntax, Broker provides a shorthand for common searches. - -======================= ========================================================== -syntax meaning -======================= ========================================================== -``db[-1]`` most recent header -``db[-5]`` fifth most recent header -``db[-5:]`` all of the last five headers -``db[108]`` header with scan ID 108 (if ambiguous, most recent is found) -``db[[108, 109, 110]]`` headers with scan IDs 108, 109, 110 -``db['acsf3rf']`` header with unique ID (uid) beginning with ``acsf3rf`` -======================= ========================================================== - -Aside: Scan ID vs. Unique ID ----------------------------- - -Notice that there are two IDs in play: the "scan ID" and the "unique ID." The -scan ID is a counting number. Some users reset it to 1 between experiments, -so it is not a good unique identifier for data --- it is just a convenience. -In the case of duplicates, Broker returns the most recent match. - -As explained above, the unique ID is randomly-generated string that is -statistically guaranteed to uniquely identify a dataset forever. The Broker -accepts a partial unique ID --- the first 5-6 characters are virtually always -enough to identify a data set. - -Time-based Queries ------------------- - -Runs that took place sometime in a given time interval are also supported. - -======================================================= ====================================== -syntax meaning -======================================================= ====================================== -``db(start_time='2015-01')`` all headers from January 2015 or later -``db(start_time='2015-01-05', stop_time='2015-01-10')`` between January 5 and 10 -======================================================= ====================================== - -Filters -------- - -.. versionadded:: v0.6.0 - -To restrict seraches by user, project, date, plan_name, or any other parameter, -add a "filter" to the Broker. - -.. code-block:: python - - # Restrict future searches. - db.add_filter(user='Dan') - db.add_filter(start_time='2015-01') - - db(sample='A') # becomes db(sample='A', user='Dan', start_time='2015-01') - - # Clear all filters. - db.clear_filters() - -Any query passed to ``db.add_filter()`` is stashed and "AND-ed" with all future -queries. You can also review or alter the filters through the ``db.filters`` -property, a list of queries (that is, a list of dicts formatted like MongoDB -queries). - -Aliases -------- - -.. versionadded:: v0.6.0 - -To "save" a search for easy resuse, you can create an alias. It may be -convenient to define these in a startup file. - -.. code-block:: python - - db.alias('cal', purpose='calibration') - - db.cal # -> db(purpose='calibration') - -A "dynamic alias" maps the alias to a function that returns a query. - -.. code-block:: python - - # Get headers from the last 24 hours. - import time - db.dynamic_alias('today', - lambda: {'start_time': start_time=time.time() - 24*60*60}) - - # Get headers where the 'user' field matches the current logged-in user. - import getpass - db.dynamic_alias('mine', lambda: {'user': getpass.getuser()}) - -Aliases are stored in ``db.aliases`` (a dictionary mapping alias names to -queries or functions that return queries) where they can be reviewed or -deleted. - -Complex Queries ---------------- - -Finally, for advanced queries, the full MongoDB query language is supported. -Here are just a few examples: - -=========================================== ============================================================ -syntax meaning -=========================================== ============================================================ -``db(sample={'$exists': True})`` headers that include a custom metadata field labeled ``sample`` -``db(plan_name={'$ne': 'relative_scan'})`` headers where the type of scan was not a ``relative_scan`` -=========================================== ============================================================ - -See the -`MongoDB query documentation `_ -for more. diff --git a/databroker/_sources/searching.txt b/databroker/_sources/searching.txt deleted file mode 100644 index 92516f9c41..0000000000 --- a/databroker/_sources/searching.txt +++ /dev/null @@ -1,268 +0,0 @@ -Searching for Data -****************** - -The result of a search is a :ref:`header `, a bundle of metadata about -a given run. In a later section, :doc:`fetching`, we will use the header to -retrieve the data itself. Headers are also useful for quickly reviewing -metadata and generating summaries and logs. - -Search Examples ---------------- - -In these examples, we will collect data using bluesky and then access it -from the databroker. This illustrates how metadata provided at collection time -can be used to search and filter the data during later analysis. - -You do not need to be familiar with bluesky's usage to follow the gist these -examples. For a more detailed understanding, refer to the sections on -`basic usage `_ and -`recording metadata `_. - -By Unique ID -++++++++++++ - -The surest and most direct way to get particular header is to look it up by its -unique ID. This ID is guaranteed to uniquely identify the run forever. - -The RunEngine returns a list of the unique ID(s) when it completes execution. - -.. code-block:: python - - # In all these examples we assume a RunEngine instance, RE, is defined. - # See link to bluesky 'basic usage' documentation above. - - # We also assume that a Broker instance, db, is defined. - # See previous section on configuration. - - uids = RE(some_plan()) - headers = db[uids] - -We could also write down the first 5-6 characters in a uid and use it to look -up the header later. Each unique ID is a randomly-generated string like - -.. code-block:: python - - 'cf24798b-ed6e-4d44-b529-7199fcec41cc' - -but the first 5-6 are virtually always enough to uniquely identify a run. The -databroker accepts a partial uid: - -.. code-block:: python - - db['cf2479'] - -If there is ambiguity (two uids starting with the same couple characters) the -Broker will raise an error. But, again, 5-6 characters are virtually always -sufficient. - -By Plan Name, Detector, or Motor -++++++++++++++++++++++++++++++++ - -Suppose we execute several experiments ("plans", in bluesky jargon) like so. - -.. code-block:: python - - from bluesky.plans import count, scan, relative_scan - from bluesky.examples motor, det # simulated motor and detector - - RE(count([det])) # 1 - RE(scan([det], motor, -1, 1, 5)) # 2 - RE(relative_scan([det], motor, 1, 10, 10)) # 3 - RE(scan([det], motor, -1, 1, 1000)) # 4 - -We can search by ``plan_name``, which is always automatically recorded in the -metadata. - -.. code-block:: python - - relative_scans = db(plan_name='relative_scan') # 3 - absolute_scans = db(plan_name='scan') # 2 and 4 - -We can also search by ``motors`` or ``detectors``. (All built-in plans provide -this metadata automatically. User-defined plans may or may not provide it.) - -.. code-block:: python - - runs_using_motor = db(motors='motor') # 2, 3, and 4 - runs_using_det = db(detectors='det') # all - -To be precise, ``detectors='det'`` means, "The detector ``det`` is included -in the *list* of detectors used." - -We can also narrow the search by certain plan-specific metadata, like the -number of steps in a scan. - -.. code-block:: python - - long_scan = db(plan_name='scan', num_steps > 50) # 4 - -These may be combined with time-based parameters (presented later below) to -restrict the search to the previous day or week. - -By Custom Metadata Fields -+++++++++++++++++++++++++ - -Again, suppose we execute several plans. This time, we provide some custom -metadata including person operating the equipment and, in some cases, about the -sample and the purpose of each run. - -.. code-block:: python - - from bluesky.plans import count, scan, relative_scan - from bluesky.examples motor, det # simulated motor and detector - - # This adds {'operator': 'Ken'} to all future runs, unless overridden. - RE.md['operator'] = 'Ken' - - RE(count([det]), purpose='calibration', sample='A') - RE(scan([det], motor, 1, 10, 10), operator='Dan') # temporarily overrides Ken - RE(count([det]), sample='A') # (now back to Ken) - RE(count([det]), sample='B') - - RE.md['operator'] = 'Dan' - - RE(count([det]), purpose='calibration') - RE(scan([det], motor, 1, 10, 10)) - - del RE.md['operator'] # clean up by un-setting operator - -We can search on any of these custom fields. (The words 'operator' and -'purpose' have no special significance to bluesky or databroker --- arbitrary -fields could have been used.) - -.. code-block:: python - - db(sample='A') # return both runs that used sample A - db(purpose='calibration', sample='A') # returns sample A calibration run - db(purpose='calibration') # returns the two calibration runs - db(operator='Dan') # returns three runs by Dan - -Full Text Search ----------------- - -Calling ``db`` with a positional argument like - -.. code-block:: python - - db('calibration') - -performs a full-text search and returns any headers with the value -``'calibration'`` in any field. - -Presently, it searches the full text of Run Start documents, which in the vast -majority of cases contains the metadata one would want to base a search on. In -the future it might be extended to search all fields in the header, depending -on performance considerations. - -Searching by ID or Recency --------------------------- - -With Python's slicing syntax, Broker provides a shorthand for common searches. - -======================= ========================================================== -syntax meaning -======================= ========================================================== -``db[-1]`` most recent header -``db[-5]`` fifth most recent header -``db[-5:]`` all of the last five headers -``db[108]`` header with scan ID 108 (if ambiguous, most recent is found) -``db[[108, 109, 110]]`` headers with scan IDs 108, 109, 110 -``db['acsf3rf']`` header with unique ID (uid) beginning with ``acsf3rf`` -======================= ========================================================== - -Aside: Scan ID vs. Unique ID ----------------------------- - -Notice that there are two IDs in play: the "scan ID" and the "unique ID." The -scan ID is a counting number. Some users reset it to 1 between experiments, -so it is not a good unique identifier for data --- it is just a convenience. -In the case of duplicates, Broker returns the most recent match. - -As explained above, the unique ID is randomly-generated string that is -statistically guaranteed to uniquely identify a dataset forever. The Broker -accepts a partial unique ID --- the first 5-6 characters are virtually always -enough to identify a data set. - -Time-based Queries ------------------- - -Runs that took place sometime in a given time interval are also supported. - -======================================================= ====================================== -syntax meaning -======================================================= ====================================== -``db(start_time='2015-01')`` all headers from January 2015 or later -``db(start_time='2015-01-05', stop_time='2015-01-10')`` between January 5 and 10 -======================================================= ====================================== - -Filters -------- - -.. versionadded:: v0.6.0 - -To restrict seraches by user, project, date, plan_name, or any other parameter, -add a "filter" to the Broker. - -.. code-block:: python - - # Restrict future searches. - db.add_filter(user='Dan') - db.add_filter(start_time='2015-01') - - db(sample='A') # becomes db(sample='A', user='Dan', start_time='2015-01') - - # Clear all filters. - db.clear_filters() - -Any query passed to ``db.add_filter()`` is stashed and "AND-ed" with all future -queries. You can also review or alter the filters through the ``db.filters`` -property, a list of queries (that is, a list of dicts formatted like MongoDB -queries). - -Aliases -------- - -.. versionadded:: v0.6.0 - -To "save" a search for easy resuse, you can create an alias. It may be -convenient to define these in a startup file. - -.. code-block:: python - - db.alias('cal', purpose='calibration') - - db.cal # -> db(purpose='calibration') - -A "dynamic alias" maps the alias to a function that returns a query. - -.. code-block:: python - - # Get headers from the last 24 hours. - import time - db.dynamic_alias('today', - lambda: {'start_time': start_time=time.time() - 24*60*60}) - - # Get headers where the 'user' field matches the current logged-in user. - import getpass - db.dynamic_alias('mine', lambda: {'user': getpass.getuser()}) - -Aliases are stored in ``db.aliases`` (a dictionary mapping alias names to -queries or functions that return queries) where they can be reviewed or -deleted. - -Complex Queries ---------------- - -Finally, for advanced queries, the full MongoDB query language is supported. -Here are just a few examples: - -=========================================== ============================================================ -syntax meaning -=========================================== ============================================================ -``db(sample={'$exists': True})`` headers that include a custom metadata field labeled 'color' -``db(plan_name={'$ne': 'relative_scan'})`` headers where the type of scan was not a ``relative_scan`` -=========================================== ============================================================ - -See the -`MongoDB query documentation `_ -for more. diff --git a/databroker/_sources/v1/api.rst.txt b/databroker/_sources/v1/api.rst.txt deleted file mode 100644 index eeb435dafd..0000000000 --- a/databroker/_sources/v1/api.rst.txt +++ /dev/null @@ -1,304 +0,0 @@ -***************** -API Documentation -***************** - -We use the :class:`Broker` to pose queries for saved data sets ("runs"). A -search query returns a lazy-loaded iterable of :class:`Header` objects. -Each :class:`Header` encapsulates the metadata for one run. It provides -convenient methods for exploring that metadata and loading the full data. - -.. currentmodule:: databroker - -The Broker object ------------------ - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker - -Making a Broker -=============== - -You can instantiate a :class:`Broker` by passing it a dictionary of -configuration or by providing the name of a configuration file on disk. - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.from_config - Broker.named - -Click the links the table above for details and examples. - -Searching -========= - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.__call__ - Broker.__getitem__ - -For some :class:`Broker` instance named ``db``, ``db()`` invokes -:meth:`Broker.__call__` and ``db[]`` invokes :meth:`Broker.__getitem__`. -Again, click the links the table above for details and examples. - -Loading Data -============ - -These methods are an older way to access data, like this: - -.. code-block:: python - - header = db[-1] - db.get_table(header) - -The newer :class:`Header` methods, :ref:`documented later on this page -` are more convenient. - -.. code-block:: python - - header = db[-1] - header.table() - -(Notice that we only had to type ``db`` once.) However, these are -still useful to loading data from *multiple* headers at once, which -the new methods cannot do: - -.. code-block:: python - - headers = db[-10:] # the ten most recent runs - db.get_table(headers) - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.get_documents - Broker.get_events - Broker.get_table - Broker.get_images - Broker.restream - Broker.process - - -The broker also has a number of methods to introspect headers: - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.get_fields - Broker.stream_names_given_header - - -Saving Data -=========== - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.insert - -Configuring Filters and Aliases -=============================== - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.add_filter - Broker.clear_filters - Broker.alias - Broker.dynamic_alias - -This current list of filters and aliases is accessible via the attributes -:class:`Broker.filters` and :class:`Broker.aliases` respectively. Again, click -the links in the table for examples. - -Export Data to Another Broker -============================= - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.export - Broker.export_size - - -.. _controlling_return_type: - -Advanced: Controlling the Return Type -===================================== - -The attribute :class:`Broker.prepare_hook` is a function with the signature -``f(name, doc)`` that is applied to every document on its way out. - -By default :class:`Broker.prepare_hook` is set to -:func:`wrap_in_deprecated_doct`. The resultant objects issue warnings if -users attempt to access items with dot access like ``event.data`` instead of -dict-style lookup like ``event['data']``. To restore the previous behavior (i.e. -suppress the warnings on dot access), set :class:`Broker.prepare_hook` to -:func:`wrap_in_doct`. - -To obtain plain dictionaries, set :class:`Broker.prepare_hook` to -``lambda name, doc: copy.deepcopy(doc)``. (Copying of is recommended because -the underlying objects are cached and mutable.) - -In a future release of databroker, the default return type may be changed to -plain dictionaries for simplicity and improved performance. - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - wrap_in_deprecated_doct - wrap_in_doct - -.. _header_api: - -The Header object ------------------ - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Header - -Metadata -======== - -The :class:`Header` bundles together the metadata of a run, accessible via the -attributes corresponding to the underlying documents: - - -* :class:`Header.start` -* :class:`Header.stop` - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Header.uid - Header.start - Header.stop - Header.descriptors - -Measurements are organized into "streams" of asynchronously collected data. The -names of all the streams are listed in the attribute -:attr:`Header.stream_names`. - -.. note:: - - It helps to understand how data and metadata are organized in our document - model. This is covered well in `this section of the bluesky documentation - `_. - -The information in these documents is a lot to navigate. Convenience methods -make it easier to extract some key information: - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Header.fields - Header.devices - Header.config_data - Header.stream_names - -Data -==== - -The :class:`Header` provides various methods for loading the 'Event' documents, -which may be large. They all access the same data, presented in various ways -for convenience. - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Header.table - Header.data - Header.documents - Header.events - Header.xarray - Header.xarray_dask - -All of the above accept an argument called ``stream_name``, which distinguishes -concurrently-collected stream of data. (Typical names include 'primary' and -'baseline'.) A list of all a header's stream names is accessible via the -attribute :class:`Header.stream_names`, a list. - -To request data from *all* event streams at once, use the special constant -:data:`databroker.ALL`. - -.. _configuration_utilities: - -Configuration Utilities ------------------------ - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - list_configs - lookup_config - temp - Broker.name - Broker.get_config - Broker.stats - -Back- and Forward-Compat Accessors ----------------------------------- - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.v1 - Broker.v2 - -Internals ---------- - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.reg - Broker.fetch_external - -Deprecated ----------- - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.stream - Header.stream - Broker.fs - Header.get - Header.items - Header.keys - Header.values - -Removed -------- - -These functions and methods now raise ``NotImplementedError`` if called. - -.. autosummary:: - :toctree: ../generated - :nosignatures: - - Broker.fill_event - Broker.fill_events - temp_config diff --git a/databroker/_sources/v1/archiver.rst.txt b/databroker/_sources/v1/archiver.rst.txt deleted file mode 100644 index 6cb34b524a..0000000000 --- a/databroker/_sources/v1/archiver.rst.txt +++ /dev/null @@ -1,74 +0,0 @@ -.. currentmodule:: databroker - - -********************************** -Connection with Archiver Appliance -********************************** - -The Archiver Appliance is an EPICS logging system developed in a collaboration -of SLAC, BNL, and MSU for collecting and storing measurements from various -control devices. Data retrieval is based on the client-server interface using -HTTP requests. Large-scale accelerators and experimental facilities may -maintain multiple archivers. Within the databroker application, each archiver -is represented by an ArchiverEventSource that needs to be configured and -registred with the Broker. The following sections highlight two major topics: -ArchiverEventSource configuration and data retrival based on the databroker -interface. - -Configuration -------------- - -ArchiverEventSources is configured via :class:`Broker` by extending the -databroker configuration file with a list of archiver entries. For example, the -following ``cxs_with_archiver.yml`` file illustrates the configuration of two -ArchiverEventSources, ``arch_csx`` and ``arch_acc``: - -.. code-block:: yaml - - event_sources: - - config: - name: 'arch_acc' - url: 'http://arcapp01.cs.nsls2.local:17668' - timezone: 'US/Eastern' - pvs: - pv2: 'UT:SB1-Cu:1{}T:Prmry-I' - module: 'databroker.eventsource.archiver' - class: 'ArchiverEventSource' - - config: - name: 'arch_csx' - url: 'http://xf23id-ca.cs.nsls2.local:17668' - timezone: 'US/Eastern' - module: 'databroker.eventsource.archiver' - class: 'ArchiverEventSource' - -According to this file, each ArchiverEventSource is defined with four -configuration key-value pairs : - -* name: user-defined name of the Archiver Appliance archiver -* url: address of the Archiver Appliance Retrieval server -* timezone: time zone -* pvs: dictionary mapping user-defined names to EPICS PVs - -A pvs dictionary can be extended via :class:`Broker` as: - -.. code-block:: python - - db = Broker.named('csx_with_archivers') - arch_csx = db.event_sources_by_name['arch_csx'] - arch_csx.pvs.update({'pv1': 'XF:23ID-ID{BPM}Val:PosXS-I'}) - - - -Data Retrieval --------------- - -After integrating ArchiverEventSources with :class:`Broker`, PV data can be -retrieved with the standard :meth:`Header.table` method: - -.. code-block:: python - - # select header - hdr = db[69209] - - stream_name = 'pv1' - df = hdr.table(stream_name=stream_name) diff --git a/databroker/_sources/v1/configuration.rst.txt b/databroker/_sources/v1/configuration.rst.txt deleted file mode 100644 index 680aad89fc..0000000000 --- a/databroker/_sources/v1/configuration.rst.txt +++ /dev/null @@ -1,116 +0,0 @@ -.. _v0_configuration: - -************* -Configuration -************* - -Configuration files make it easy to quickly set up :class:`Broker` instances -with the expression ``Broker.named('example')`` where 'example' is the name of -a configuration file. - -Search Path ------------ - -The databroker looks for configuration files in three locations, in this order: - -* ``~/.config/databroker`` (under the user's home directory) -* ``python/../etc/databroker``, where ``python`` is the current Python binary - reported by ``sys.executable`` (This allows config to be provided inside a - virtual environment.) -* ``/etc/databroker/`` - -NOTE: For Windows, we only look in: ``%APPDATA%\databroker``. - -A configuration file must be located in one of these directories, and it must -be named with the extension ``.yml``. Configuration files are formatted as YAML -files. - -Examples --------- - -This configuration file sets up a simple databroker backed by sqlite files. -This can be used immediately with no extra setup or installation. - -.. code-block:: yaml - - description: 'lightweight personal database' - metadatastore: - module: 'databroker.headersource.sqlite' - class: 'MDS' - config: - directory: 'some_directory' - timezone: 'US/Eastern' - assets: - module: 'databroker.assets.sqlite' - class: 'Registry' - config: - dbpath: 'some_directory/assets.sqlite' - -This configuration file sets up a databroker that connects to a MongoDB server. -This requires more work to set up. - -.. code-block:: yaml - - description: 'heavyweight shared database' - metadatastore: - module: 'databroker.headersource.mongo' - class: 'MDS' - config: - host: 'localhost' - port: 27017 - database: 'some_example_database' - timezone: 'US/Eastern' - assets: - module: 'databroker.assets.mongo' - class: 'Registry' - config: - host: 'localhost' - port: 27017 - database: 'some_example_database' - -In these examples, the classes used happen to be from the ``databroker`` -package itself, but classes from other packages can be used just as well, as -long as they present the expected API. (This API is not yet documented outside -of the source code.) - -Registering Handlers --------------------- - -Configuration files may optionally include a section specifying 'handlers', -classes that load externally stored data. These may be registered at runtime or -here in configuration. - -.. code-block:: yaml - - handlers: - FOO: - module: 'databroker.assets.path_only_handlers' - class: 'RawHandler' - -Coping with Moved Files or Different Mount Points -------------------------------------------------- - -Optionally, you may set a root_map, which comes in handy when the handlers -involves mounted files that have been moved to a different mount point in the -file system. - -.. code-block:: yaml - - root_map: - old_root: new_root - old_root2: new_root2 - -where ``old_root`` and ``old_root2`` are the old mount points and ``new_root`` -and ``new_root2`` their respective new mount points. - -.. warning:: - - Future versions of databroker will provide better support for multiple - asset registries and multiple sources of Event data, and this configuration - file format will change. If possible, old configuration files will still be - supported. - -Helper Functions ----------------- - -See :ref:`configuration_utilities` in the API documentation. diff --git a/databroker/_sources/v1/creating.rst.txt b/databroker/_sources/v1/creating.rst.txt deleted file mode 100644 index c3a26fb6b2..0000000000 --- a/databroker/_sources/v1/creating.rst.txt +++ /dev/null @@ -1,46 +0,0 @@ -*********************** -Creating a New Database -*********************** - -The databroker is above all an *interface*, which is not beholden to a specific -storage technology. Several different storage backends are included in the -databroker package (MongoDB, JSON, sqlite, HDF5) and fully tested. But as of -this writing, the maintainers of databroker rely exclusively on MongoDB for -large-scale production, and it has an additional production-oriented feature, -described here. Therefore, these instructions apply to the MongoDB storage -backend only. Brokers configured with other backends may ignore these -instructions. - -Creation in MongoDB -------------------- - -MongoDB itself does not require an explicit step for creating a new database. -It will quietly create one for you the first time you connect to it. This -can be dangerous (or, at best, messy) because an unnoticed typo can result in -an accidental, separate database. To enforce more explicit intention in this -process, databroker looks for a special "sentinel" collection when it connects -to a Mongo database. If it does not find one, it will refuse to connect to that -database until the user installs the sentinel --- thereby affirming, "Yes, I -intend to create a new database." The sentinel also contains a version number, -which may be useful for migrations in the event of any future changes to the -specification. - -The creation of a database in MongoDB is simple. Simply create a configuration -as described in :doc:`configuration`. The only requirement is that the MongoDB -instance is running on the machine specified. The database need not exist yet. -The next step is to install a version sentinel. This is done as follows: - -.. code-block:: python - - import databroker - - # Instantiate databroker instance - from databroker import Broker - db = Broker.named(config_name) - - # install sentinels - databroker.assets.utils.install_sentinels(db.reg.config, version=1) - -where ``config_name`` is the name of your configuration and ``version=1`` -refers to the version of asset registry you are using (it is currently ``1`` as -of this writing). diff --git a/databroker/_sources/v1/index.rst.txt b/databroker/_sources/v1/index.rst.txt deleted file mode 100644 index 17b26e3e89..0000000000 --- a/databroker/_sources/v1/index.rst.txt +++ /dev/null @@ -1,14 +0,0 @@ -.. _v1_index: - -******************* -Version 1 Interface -******************* - -.. toctree:: - :maxdepth: 1 - - tutorial - api - configuration - creating - archiver diff --git a/databroker/_sources/v1/tutorial.rst.txt b/databroker/_sources/v1/tutorial.rst.txt deleted file mode 100644 index f1eb3af284..0000000000 --- a/databroker/_sources/v1/tutorial.rst.txt +++ /dev/null @@ -1,242 +0,0 @@ -.. currentmodule:: databroker - - -******** -Tutorial -******** - -The databroker is a tool to access data from many sources through a unified -interface. It emphasizes rich searching capabilities and handling multiple -concurrent "streams" of data in an organized way. - - -.. ipython:: python - :suppress: - - import os - import yaml - from databroker.tests.test_config import EXAMPLE as config - os.makedirs('~/.config/databroker', exist_ok=True) - path = os.path.expanduser('~/.config/databroker/example.yml') - with open(path, 'w') as f: - yaml.dump(config, f) - from databroker import Broker - db = Broker.named('example') - from bluesky import RunEngine - from bluesky.plans import scan - from ophyd.sim import det, motor - RE = RunEngine({}) - RE.subscribe(db.insert) - for _ in range(5): - RE(scan([det], motor, 1, 5, 5)) - -Basic Walkthrough ------------------ - -Get a Broker -============ - -List the names of available configurations. - -.. ipython:: python - - from databroker import list_configs - - list_configs() - -If this list is empty, no one has created any configuration files yet. See the -section on :doc:`configuration`. - -Make a databroker using one of the configurations. - -.. ipython:: python - - from databroker import Broker - db = Broker.named('example') - -Load Data as a Table -==================== - -Load the most recently saved run. - -.. ipython:: python - - header = db[-1] - -The result, a :class:`Header`, encapsulates the metadata from this run. Loading -the data itself can be a longer process, so it's a separate step. For scalar -data, the most convenient method is: - -.. ipython:: python - - header.table() - -This object is DataFrame, a spreadsheet-like object provided by the library -`pandas `_. - -.. note:: - - For Python novices we point out that ``header`` above is an arbitrary - variable name. It could have been: - - .. code-block:: python - - h = db[-1] - h.table() - - or even in one line: - - .. code-block:: python - - db[-1].table() - -Do Analysis or Export -===================== - -DataFrames can be used to perform fast computations on labeled data, such as - -.. ipython:: python - - t = header.table() - t.mean(numeric_only=True) - t['det'] / t['motor'] - -or export to a file. - -.. ipython:: python - - t.to_csv('data.csv') - -.. ipython:: python - :suppress: - - # Clean up - !rm data.csv - - -Load Data Lazily (Good for Image Data) -====================================== - -The :class:`Header.table` method is just one way to load the data. Another is -:class:`Header.data`, which loads data for one specific field (i.e., one column -of the table) in a "lazy", streaming fashion. - -.. ipython:: python - - data = header.data('det') - data # This a 'generator' that will load data when we loop through it. - for point in data: - # 'Process' the data one point at a time. - # Here we'll just print it. - print(point) - -The :class:`Header.data` method is suitable for loading image data. See -the :doc:`api` for more methods. - -Explore Metadata -================ - -Everything recorded at the start of the run is in ``header.start``. - -.. ipython:: python - - header.start - -Information only knowable at the end, like the exit status (success, abort, -fail) is stored in ``header.stop``. - -.. ipython:: python - - header.stop - -Metadata about the devices involved and their configuration is stored in -``header.descriptors``, but that is quite a lot to dig through, so it's useful -to start with some convenience methods that extract the list of devices or the -fields that they reported: - -.. ipython:: python - - header.devices() - header.fields() - -To extract configuration data recorded by a device: - -.. ipython:: python - - header.config_data('motor') - -(A realistic example might report, for example, exposure_time or zero point.) - - -Searching ---------- - -The "slicing" (square bracket) syntax is a quick way to search based on -relative indexing, unique ID, or counting number scan_id. Examples: - -.. code-block:: python - - # Get the most recent run. - header = db[-1] - - # Get the fifth most recent run. - header = db[-5] - - # Get a list of all five most recent runs, using Python slicing syntax. - headers = db[-5:] - - # Get a run whose unique ID ("RunStart uid") begins with 'x39do5'. - header = db['x39do5'] - - # Get a run whose integer scan_id is 42. Note that this might not be - # unique. In the event of duplicates, the most recent match is returned. - header = db[42] - -Calling a Broker like a function (with parentheses) accesses richer searches. -Common search parameters include ``plan_name``, ``motor``, and ``detectors``. -Any user-provided metadata can be used in a search. Examples: - -.. code-block:: python - - # Search by plan name. - headers = db(plan_name='scan') - - # Search for runs involving a motor with the name 'eta'. - headers = db(motor='eta') - - # Search for runs operated by a given user---assuming this metadata was - # recorded in the first place! - headers = db(operator='Dan') - - # Search by time range. (These keywords have a special meaning.) - headers = db(since='2015-03-05', until='2015-03-10') - -Full-text search is also supported, for MongoDB-backed deployments. (Other -deployments will raise :class:`NotImplementedError` if you try this.) - -.. code-block:: python - - # Perform text search on all values in the Run Start document. - headers = db('keyword') - -Note that partial words are not matched, but partial phrases are. For example, -'good' will match to 'good sample' but 'goo' will not. - -Unlike the "slicing" (square bracket) queries, rich searches can return an -unbounded number of results. To avoid slowness, the results are loaded -"lazily," only as needed. Here's an example of what works and what doesn't. - -.. ipython:: python - :okexcept: - - headers = db(plan_name='scan') - headers - headers[2] # Fails! The results are not a list. - list(headers)[2] # This works, but might be slow if the results are large. - -Looping through them loads one at a time, conserving memory. - -.. ipython:: python - - for header in headers: - print(header.table()['det'].mean()) diff --git a/databroker/_sources/v2/administrator/index.rst.txt b/databroker/_sources/v2/administrator/index.rst.txt deleted file mode 100644 index 3dd3d5f0e2..0000000000 --- a/databroker/_sources/v2/administrator/index.rst.txt +++ /dev/null @@ -1,259 +0,0 @@ -*************************** -Administrator Documentation -*************************** - -When databroker is imported, it discovers catalogs available on the system. -User can list the discovered catalogs by importing a special global -``databroker.catalog`` object and listing its entries. - -.. code:: python - - from databroker import catalog - list(catalog) # a list of strings, names of sub-catalogs - -which can be accessed like - -.. code:: python - - catalog['SOME_SUB_CATALOG'] - -DataBroker assembles this list of catalogs by looking for: - -1. Old-style "databroker v0.x" YAML configuration files, for backward-compatibility -2. Intake-style catalog YAML files, which have different fields -3. Python packages that advertise catalogs via the ``intake.catalogs`` - entrypoint - -Old-style databroker configuration files -======================================== - -DataBroker v0.x used a custom YAML-based configuration file. See -:ref:`v0_configuration`. For backward-compatibility, configuration files -specifying MongoDB storage will be discovered and included in -``databroker.catalog``. - -Migrating sqlite or HDF5 storage --------------------------------- - -The implementation in ``databroker.v0`` interfaces with storage in MongoDB, -sqlite, or HDF5. The implementations in ``databroker.v1`` and -``databroker.v2`` drop support for sqlite and HDF5 and add support for JSONL_ -(newline-delimited JSON) and msgpack_. For binary file-based storage, we -recommend using msgpack. Data can be migrated from sqlite or HDF5 to msgpack -like so: - -.. code-block:: python - - from databroker import Broker - import suitcase.msgpack - - # If the config file associated with YOUR_BROKER_NAME specifies sqlite or - # HDF5 storage, then this will return a databroker.v0.Broker instance. - db = Broker.named(YOUR_BROKER_NAME) - # Loop through every run in the old Broker. - for run in db(): - # Load all the documents out of this run from their existing format and - # write them into one file located at - # `/.msgpack`. - suitcase.msgpack.export(run.documents(), DESTINATION_DIRECTORY) - -In the next section, we'll create a "catalog YAML file" to make this data -discoverable by databroker. - -Intake-style Catalog YAML Files -=============================== - -Search Path ------------ - -Use the convenience function :func:`catalog_search_path`. Place catalog YAML -files in one of these locations to make them discoverable by intake and, in -turn, by databroker. - -.. code:: python - - from databroker import catalog_search_path - catalog_search_path() # result will vary depending on OS and environment - -Structure ---------- - -The general structure of a catalog YAML file is a nested dictionary of -data "sources". Each source name is mapped to information for accessing that -data, which includes a type of "driver" and some keyword arguments to pass to -it. A "driver" is generally associated with a particular storage format. - -.. code:: yaml - - sources: - SOME_NAME: - driver: SOME_DRIVER - args: - SOME_PARAMETER: VALUE - ANOTHER_PARAMETER: VALUE - ANOTHER_NAME: - driver: SOME_DRIVER - args: - SOME_PARAMETER: VALUE - ANOTHER_PARAMETER: VALUE - -As shown, multiple sources can be specified in one file. All sources found in -all the YAML files in the search path will be included as top-level entries in -``databroker.catalog``. - -Arguments ---------- - -All databroker "drivers" accept the following arguments: - -* ``handler_registry`` --- - If ommitted or ``None``, the result of - :func:`~databroker.core.discover_handlers` is used. See - :doc:`event-model:external` for background on the role of "handlers". -* ``root_map`` --- - This is passed to :func:`event_model.Filler` to account for temporarily - moved/copied/remounted files. Any resources which have a ``root`` matching a - key in ``root_map`` will be loaded using the mapped value in ``root_map``. -* ``transforms`` --- - A dict that maps any subset of the keys {start, stop, resource, descriptor} - to a function that accepts a document of the corresponding type and - returns it, potentially modified. This feature is for patching up - erroneous metadata. It is intended for quick, temporary fixes that - may later be applied permanently to the data at rest - (e.g., via a database migration). - -Specific drivers require format-specific arguments, shown in the following -subsections. - -Msgpack Example ---------------- - -Msgpack_ is a binary file format. - -.. code:: yaml - - sources: - ENTRY_NAME: - driver: bluesky-msgpack-catalog - args: - paths: - - "DESTINATION_DIRECTORY/*.msgpack" - -where ``ENTRY_NAME`` is a name of the entry that will appear in -``databroker.catalog``, and ``DESTINATION_DIRECTORY`` is a directory of msgpack -files generated by suitcase-msgpack_, as illustrated in the previous section. - -Note that the value of ``paths`` is a list. Multiple directories can be grouped -into one "source". - -JSONL (Newline-delimited JSON) Example --------------------------------------- - -JSONL_ is a text-based format in which each line is a -valid JSON. Unlike ordinary JSON, it is suitable for streaming. This storage is -much slower than msgpack, but the format is human-readable. - -.. code:: yaml - - sources: - ENTRY_NAME: - driver: bluesky-jsonl-catalog - args: - paths: - - "DESTINATION_DIRECTORY/*.jsonl" - -where ``ENTRY_NAME`` is a name of the entry that will appear in -``databroker.catalog`` and ``DESTINATION_DIRECTORY`` is a directory of -newline-delimited JSON files generated by suitcase-jsonl_. - -Note that the value of ``paths`` is a list. Multiple directories can be grouped -into one "source". - -MongoDB Example ---------------- - -MongoDB_ is the recommended storage format for -large-scale deployments because it supports fast search. - -.. code:: yaml - - sources: - ENTRY_NAME: - driver: bluesky-mongo-normalized-catalog - args: - metadatastore_db: mongodb://HOST:PORT/MDS_DATABASE_NAME - asset_registry_db: mongodb://HOST:PORT/ASSETS_DATABASE_NAME - -where ``ENTRY_NAME`` is a name of the entry that will appear in -``databroker.catalog``, and the ``mongodb://...`` URIs point to MongoDB -databases with documents inserted by suitcase-mongo_. - -The driver's name, ``bluesky-mongo-normalized-catalog``, differentiates it from -the ``bluesky-mongo-embedded-catalog``, an experimental alternative way of -original bluesky documents into MongoDB documents and collections. It is still -under evaluation and not yet recommended for use in production. - -Python packages -=============== - -To distribute catalogs to users, it may be more convenient to provide an -installable Python package, rather than placing YAML files in specific -locations on the user's machine. To achieve this, a Python package can -advertise catalog objects using the ``'intake.catalogs'`` entrypoint. Here is a -minimal example: - -.. code:: python - - # setup.py - from setuptools import setup - - setup(name='example', - entry_points={'intake.catalogs': - ['ENTRY_NAME = example:catalog_instance']}, - py_modules=['example']) - -.. code:: python - - # example.py - - # Create an object named `catalog_instance` which is referenced in the - # setup.py, and will be discovered by databroker. How the instance is - # created, and what type of catalog it is, is completely up to the - # implementation. This is just one possible example. - - import intake - - # Look up a driver class by its name in the registry. - catalog_class = intake.registry['bluesky-mongo-normalized-catalog'] - - catalog_instance = catalog_class( - metadatastore_db='mongodb://...', asset_registry_db='mongodb://...') - -The ``entry_points`` parameter in the ``setup(...)`` is a feature supported by -Python packaging. When this package is installed, a special file inside the -distribution, ``entry_points.txt``, will advertise that is has catalogs. -DataBroker will discover these and add them to ``databroker.catalog``. Note -that databroker does *not* need to actually *import* the package to discover -its catalogs. The package will only be imported if and when the catalog is -accessed. Thus, the overhead of this discovery process is low. - -.. important:: - - Some critical details of Python's entrypoints feature: - - * Note the unusual syntax of the entrypoints. Each item is given as one long - string, with the ``=`` as part of the string. Modules are separated by - ``.``, and the final object name is preceded by ``:``. - * The right hand side of the equals sign must point to where the object is - *actually defined*. If ``catalog_instance`` is defined in - ``foo/bar.py`` and imported into ``foo/__init__.py`` you might expect - ``foo:catalog_instance`` to work, but it does not. You must spell out - ``foo.bar:catalog_instance``. - - -.. _jsonl: http://jsonlines.org/ -.. _msgpack: https://msgpack.org/index.html -.. _suitcase-mongo: https://github.com/bluesky/suitcase-mongo -.. _suitcase-jsonl: https://github.com/bluesky/suitcase-jsonl -.. _suitcase-msgpack: https://github.com/bluesky/suitcase-msgpack -.. _MongoDB: https://www.mongodb.com/ diff --git a/databroker/_sources/v2/developer/index.rst.txt b/databroker/_sources/v2/developer/index.rst.txt deleted file mode 100644 index d721ac76c6..0000000000 --- a/databroker/_sources/v2/developer/index.rst.txt +++ /dev/null @@ -1,106 +0,0 @@ -.. currentmodule:: databroker.core - -*********************** -Developer Documentation -*********************** - -Design -====== - -Intake Concepts ---------------- - -Intake has a notion of Catalogs. Catalogs are roughly dict-like. Iterating over -a Catalog yields the names of its entries, which are strings. Iterating over -``catalog.items()`` yields ``(name, Entry)`` pairs. An Entry is roughly like -a ``functools.partial`` with metadata and intake-specific semantics. When an -Entry is opened, by calling it ``entry.get()`` or, equivalently and more -succinctly, ``entry()``, it returns its content. The content could be another -Catalog or a DataSource. - -Calling ``.read()`` on a DataSource returns some in-memory representation, such -as a numpy array, pandas DataFrame, or xarray.Dataset. Calling ``.to_dask()`` -return the "lazy" dask-backed equivalent structure. - -DataBroker Concepts -------------------- - -DataBroker represents a Bluesky "Event Stream", a logical table of data, as a -DataSource, :class:`BlueskyEventStream`. Calling -:meth:`BlueskyEventStream.read` returns an xarray Dataset backed by numpy -arrays; calling :meth:`BlueskyEventStream.to_dask` returns an xarray Dataset -backed by dask arrays. - -DataBroker represents a Bluesky Run, sometimes loosely referred to as a "scan", -as a Catalog of Event Streams, :class:`BlueskyRun`. For example, the entries in -a :class:`BlueskyRun` might have the names ``'primary'`` and ``'baseline'``. -The entries always contain instances of :class:`BlueskyEventStream`. -:class:`BlueskyRun` extends the standard Catalog interface with a special -method :meth:BlueskyRun.documents`. This returns a generator that yields -``(name, doc)`` pairs, recreating the stream of documents that would have been -emitted during data acquisition. (This is akin to ``Header.documents()`` in -DataBroker v0.x.) - -:class:`BlueskyEventStream` and :class:`BlueskyRun` should never be -instantiated by the user. They have complex signatures, and they are agnostic -to the storage mechanism; they could be backed by objects in memory, files, or -databases. - -Continuing to move up the hierarchy, we get to catalogs whose Entries contain -:class:`BlueskyRun` instances. Each entry's name is the corresponding RunStart -``uid``. The Catalogs at this level of the hierarchy include: - -.. currentmodule:: databroker - -* :class:`_drivers.jsonl.BlueskyJSONLCatalog` -* :class:`_drivers.msgpack.BlueskyMsgpackCatalog` -* :class:`_drivers.mongo_normalized.BlueskyMongoCatalog` -* :class:`_drivers.mongo_embedded.BlueskyMongoCatalog` - -Notice that these are located in an internal package, ``_drivers``. Except for -testing purposes, they should never be directly imported. They should be -accessed by their name from intake's driver registry as in: - -.. code:: python - - import intake - cls = intake.registry['bluesky-jsonl-catalog'] - -At some point in the future, once the internal APIs stabilize, these classes -and their specific dependencies (msgpack, pymongo, etc.) will be moved out of -databroker into separate packages. Avoid directly importing from ``_drivers`` -so that this change will not break your code. - -Scaling Intake Catalogs ------------------------ - -To make Catalogs scale to tens of thousands of entries, override the methods: - -* ``__iter__`` -* ``__getitem__`` -* ``__contains__`` -* ``__len__`` - -A simple intake Catalog populates an internal dictionary, ``Catalog._entries``, -mapping entry names to :class:`LocalCatalogEntry` objects. This approach does -not scale to catalogs with large number of entries, where merely populating the -keys of the ``Catalog._entries`` dict is expensive. To customize the type of -``_entries`` override :meth:`Catalog._make_entries_container` and return a -dict-*like* object. This object must support iteration (looping through part or -all of the catalog in order) and random access (requesting a specific entry by -name) by implementing ``__iter__`` and ``__getitem__`` respectively. - -It should also implement ``__contains__`` because, similarly, if -``__contains__`` is specifically implemented, Python will iterate through all the -entries and check each in turn. In this case, it is likely more efficient to -implement a ``__contains__`` method that uses ``__getitem__`` to determine -whether a given key is contained. - -Finally, the Catalog itself should implement ``__len__``. If it is not -implemented, intake may obtain a Catalog's length by iterating through it -entirely, which may be costly. If a more efficient approach is possible (e.g. a -COUNT query) it should be implemented. - -.. toctree:: - - reference diff --git a/databroker/_sources/v2/developer/reference.rst.txt b/databroker/_sources/v2/developer/reference.rst.txt deleted file mode 100644 index 8bcaabc080..0000000000 --- a/databroker/_sources/v2/developer/reference.rst.txt +++ /dev/null @@ -1,58 +0,0 @@ -************* -API Reference -************* - -Core -==== - -.. autoclass:: databroker.core.Document - :members: - -.. autoclass:: databroker.core.BlueskyRun - :members: - -.. autoclass:: databroker.core.RemoteBlueskyRun - :members: - -.. autoclass:: databroker.core.BlueskyEventStream - :members: - -.. autofunction:: databroker.core.discover_handlers - -.. autofunction:: databroker.core.parse_handler_registry - -.. autofunction:: databroker.core.parse_transforms - -.. autoclass:: databroker.v2.Broker - :members: - -Utils -===== - -.. autofunction:: databroker.utils.catalog_search_path - -.. autofunction:: databroker.v2.temp - -.. autofunction:: databroker.v1.temp - -Backend-Specific Catalogs -========================= - -.. note:: - - These drivers are currently being developed in databroker itself, but - will eventually be split out into separate repositories to isolate - dependencies and release cycles. This will be done once the internal - interfaces are stable. - -.. autoclass:: databroker._drivers.jsonl.BlueskyJSONLCatalog - :members: - -.. autoclass:: databroker._drivers.mongo_embedded.BlueskyMongoCatalog - :members: - -.. autoclass:: databroker._drivers.mongo_normalized.BlueskyMongoCatalog - :members: - -.. autoclass:: databroker._drivers.msgpack.BlueskyMsgpackCatalog - :members: diff --git a/databroker/_sources/v2/index.rst.txt b/databroker/_sources/v2/index.rst.txt deleted file mode 100644 index 6acb895c7f..0000000000 --- a/databroker/_sources/v2/index.rst.txt +++ /dev/null @@ -1,20 +0,0 @@ -******************* -Version 2 Interface -******************* - -.. important:: - - DataBroker release 1.0 includes support for old-style "v1" usage and - new-style "v2" usage. This section addresses databroker's new "v2" usage. - It is still under development and subject to change in response to user - feedback. - - For the stable usage "v1" usage, see :ref:`v1_index`. See - :ref:`transition_plan` for more information. - -.. toctree:: - :maxdepth: 1 - - user/index - administrator/index - developer/index diff --git a/databroker/_sources/v2/user/index.rst.txt b/databroker/_sources/v2/user/index.rst.txt deleted file mode 100644 index 1b5a0fa26e..0000000000 --- a/databroker/_sources/v2/user/index.rst.txt +++ /dev/null @@ -1,308 +0,0 @@ -****************** -User Documentation -****************** - -.. important:: - - DataBroker release 1.0 includes support for old-style "v1" usage and - new-style "v2" usage. This section addresses databroker's new "v2" usage. - It is still under development and subject to change in response to user - feedback. - - For the stable usage "v1" usage, see :ref:`v1_index`. See - :ref:`transition_plan` for more information. - -.. ipython:: python - :suppress: - - import os - os.makedirs('data', exist_ok=True) - from bluesky import RunEngine - RE = RunEngine() - from bluesky.plans import scan - from ophyd.sim import img, motor, motor1, motor2 - from suitcase.jsonl import Serializer - from bluesky.preprocessors import SupplementalData - sd = SupplementalData(baseline=[motor1, motor2]) - RE.preprocessors.append(sd) - RE.md['proposal_id'] = 12345 - for _ in range(5): - with Serializer('data') as serializer: - uid, = RE(scan([img], motor, -1, 1, 3), serializer) - RE.md['proposal_id'] = 6789 - for _ in range(7): - with Serializer('data') as serializer: - RE(scan([img], motor, -1, 1, 3), serializer) - serializer.close() - from intake.catalog.local import YAMLFileCatalog - csx = YAMLFileCatalog('source/_catalogs/csx.yml') - # Work around intake#545. - csx._container = None - import databroker - # Monkey-patch to override databroker.catalog so we can directly - # add examples instead of taking the trouble to create and then clean up - # config files or Python packages of catalogs. - from intake.catalog.base import Catalog - databroker.catalog = Catalog() - databroker.catalog._entries['csx'] = csx - for name in ('chx', 'isr', 'xpd', 'sst', 'bmm', 'lix'): - databroker.catalog._entries[name] = Catalog() - -Walkthrough -=========== - -Find a Catalog --------------- - -When databroker is first imported, it searches for Catalogs on your system, -typically provided by a Python package or configuration file that you or an -administrator installed. - -.. ipython:: python - - from databroker import catalog - list(catalog) - -Each entry is a Catalog that databroker discovered on our system. In this -example, we find Catalogs corresponding to different instruments/beamlines. We -can access a subcatalog with square brackets, like accessing an item in a -dictionary. - -.. ipython:: python - - catalog['csx'] - -List the entries in the 'csx' Catalog. - -.. ipython:: python - - list(catalog['csx']) - -We see Catalogs for raw data and processed data. Let's access the raw one -and assign it to a variable for convenience. - -.. ipython:: python - - raw = catalog['csx']['raw'] - -This Catalog contains all the raw data taken at CSX. It contains many entries, -as we can see by checking ``len(raw)`` so listing it would take awhile. -Instead, we'll look up entries by name or by search. - -.. note:: - - As an alternative to ``list(...)``, try using tab-completion to view your - options. Typing ``catalog['`` and then hitting the TAB key will list the - available entries. - - Also, these shortcuts can save a little typing. - - .. code:: python - - # These three lines are equivalent. - catalog['csx']['raw'] - catalog['csx', 'raw'] - catalog.csx.raw # only works if the entry names are valid Python identifiers - -Look up a Run by ID -------------------- - -Suppose you know the unique ID of a run (a.k.a "scan") that we want to access. Note -that the first several characters will do; usually 6-8 are enough to uniquely -identify a given run. - -.. ipython:: python - - run = raw[uid] # where uid is some string like '17531ace' - -Each run also has a ``scan_id``. The ``scan_id`` is usually easier to remember -(it's a counting number, not a random string) but it may not be globally -unique. If there are collisions, you'll get the most recent match, so the -unique ID is better as a long-term reference. - -.. ipython:: python - - run = raw[1] - -Search for Runs ---------------- - -Suppose you want to sift through multiple runs to examine a range of datasets. - -.. ipython:: python - - query = {'proposal_id': 12345} # or, equivalently, dict(proposal_id=12345) - search_results = raw.search(query) - -The result, ``search_results``, is itself a Catalog. - -.. ipython:: python - - search_results - -We can quickly check how many results it contains - -.. ipython:: python - - len(search_results) - -and, if we want, list them. - -.. ipython:: python - - list(search_results) - -Because searching on a Catalog returns another Catalog, we refine our search -by searching ``search_results``. In this example we'll use a helper, -:class:`~databroker.queries.TimeRange`, to build our query. - -.. ipython:: python - - from databroker.queries import TimeRange - - query = TimeRange(since='2019-09-01', until='2040') - search_results.search(query) - -Other sophisticated queries are possible, such as filtering for scans that -include *greater than* 50 points. - -.. code:: python - - search_results.search({'num_points': {'$gt': 50}}) - -See MongoQuerySelectors_ for more. - -Once we have a result catalog that we are happy with we can list the entries -via ``list(search_results)``, access them individually by names as in -``search_results[SOME_UID]`` or loop through them: - -.. ipython:: python - - for uid, run in search_results.items(): - # Do stuff - ... - -Access Data ------------ - -Suppose we have a run of interest. - -.. ipython:: python - - run = raw[uid] - -A given run contains multiple logical tables. The number of these tables and -their names varies by the particular experiment, but two common ones are - -* 'primary', the main data of interest, such as a time series of images -* 'baseline', readings taken at the beginning and end of the run for alignment - and sanity-check purposes - -To explore a run, we can open its entry by calling it like a function with no -arguments: - -.. ipython:: python - - run() # or, equivalently, run.get() - -We can also use tab-completion, as in ``entry['`` TAB, to see the contents. -That is, the Run is yet another Catalog, and its contents are the logical -tables of data. Finally, let's get one of these tables. - -.. ipython:: python - - ds = run.primary.read() - ds - -This is an xarray.Dataset. You can access specific columns - -.. ipython:: python - - ds['img'] - -do mathematical operations - -.. ipython:: python - - ds.mean() - -make quick plots - -.. ipython:: python - - @savefig ds_motor_plot.png - ds['motor'].plot() - -and much more. See the documentation on xarray_. - -If the data is large, it can be convenient to access it lazily, deferring the -actual loading network or disk I/O. To do this, replace ``read()`` with -``to_dask()``. You still get back an xarray.Dataset, but it contains -placeholders that will fetch the data in chunks and only as needed, rather than -greedily pulling all the data into memory from the start. - -.. ipython:: python - - ds = run.primary.to_dask() - ds - -See the documentation on dask_. - -TODO: This is displaying numpy arrays, not dask. Illustrating dask here might -require standing up a server. - -Explore Metadata ----------------- - -Everything recorded at the start of the run is in ``run.metadata['start']``. - -.. ipython:: python - - run.metadata['start'] - -Information only knowable at the end, like the exit status (success, abort, -fail) is stored in ``run.metadata['stop']``. - -.. ipython:: python - - run.metadata['stop'] - -The v1 API stored metadata about devices involved and their configuration, -accessed using ``descriptors``, this is roughly equivalent to what is available -in ``primary.metadata``. It is quite large, - -.. ipython:: python - - run.primary.metadata - -It is a little flatter with a different layout than was returned by the v1 API. - -Replay Document Stream ----------------------- - -Bluesky is built around a streaming-friendly representation of data and -metadata. (See event-model_.) To access the run---effectively replaying the -chronological stream of documents that were emitted during data -acquisition---use the ``documents()`` method. - -.. versionchanged:: 1.2.0 - - The ``documents`` method was formerly named ``canonical``. The old name is - still supported but deprecated. - -.. ipython:: python - - run.documents(fill='yes') - -This generator yields ``(name, doc)`` pairs and can be fed into streaming -visualization, processing, and serialization tools that consume this -representation, such as those provided by bluesky. - -The keyword argument ``fill`` is required. Its allowed values are ``'yes'`` -(numpy arrays)`, ``'no'`` (Datum IDs), and ``'delayed'`` (dask arrays, still -under development). - -.. _MongoQuerySelectors: https://docs.mongodb.com/v3.2/reference/operator/query/#query-selectors -.. _xarray: https://xarray.pydata.org/en/stable/ -.. _dask: https://docs.dask.org/en/latest/ -.. _event-model: https://blueskyproject.io/event-model/ diff --git a/databroker/_sources/whats_new.rst.txt b/databroker/_sources/whats_new.rst.txt deleted file mode 100644 index a107db51f4..0000000000 --- a/databroker/_sources/whats_new.rst.txt +++ /dev/null @@ -1,54 +0,0 @@ -.. _whats_new: - -Release History -=============== - -A catalog of new features, improvements, and bug-fixes in each release. Follow -links to the relevant GitHub issue or pull request for specific code changes -and any related discussion. - -.. include:: whats_new/v1.1.0.txt -.. include:: whats_new/v1.0.6.txt -.. include:: whats_new/v1.0.5.txt -.. include:: whats_new/v1.0.4.txt -.. include:: whats_new/v1.0.3.txt -.. include:: whats_new/v1.0.2.txt -.. include:: whats_new/v1.0.1.txt -.. include:: whats_new/v1.0.0.txt -.. include:: whats_new/v0.13.3.txt -.. include:: whats_new/v0.13.2.txt -.. include:: whats_new/v0.13.1.txt -.. include:: whats_new/v0.13.0.txt -.. include:: whats_new/v0.12.2.txt -.. include:: whats_new/v0.12.1.txt -.. include:: whats_new/v0.12.0.txt -.. include:: whats_new/v0.11.3.txt -.. include:: whats_new/v0.11.2.txt -.. include:: whats_new/v0.11.1.txt -.. include:: whats_new/v0.11.0.txt -.. include:: whats_new/v0.10.0.txt -.. include:: whats_new/v0.9.4.txt -.. include:: whats_new/v0.9.3.txt -.. include:: whats_new/v0.9.2.txt -.. include:: whats_new/v0.9.1.txt -.. include:: whats_new/v0.9.0.txt -.. include:: whats_new/v0.8.4.txt -.. include:: whats_new/v0.8.3.txt -.. include:: whats_new/v0.8.2.txt -.. include:: whats_new/v0.8.1.txt -.. include:: whats_new/v0.8.0.txt -.. include:: whats_new/v0.7.0.txt -.. include:: whats_new/v0.6.2.txt -.. include:: whats_new/v0.6.1.txt -.. include:: whats_new/v0.6.0.txt -.. include:: whats_new/v0.5.0.txt -.. include:: whats_new/v0.4.1.txt -.. include:: whats_new/v0.4.0.txt -.. include:: whats_new/v0.3.3.txt -.. include:: whats_new/v0.3.2.txt -.. include:: whats_new/v0.3.1.txt -.. include:: whats_new/v0.3.0.txt -.. include:: whats_new/v0.2.2.txt -.. include:: whats_new/v0.2.1.txt -.. include:: whats_new/v0.2.0.txt -.. include:: whats_new/v0.0.6.txt diff --git a/databroker/_sources/whats_new.txt b/databroker/_sources/whats_new.txt deleted file mode 100644 index 8a0a7a686e..0000000000 --- a/databroker/_sources/whats_new.txt +++ /dev/null @@ -1,16 +0,0 @@ -.. _whats_new: - -Release Notes -============= - -A catalog of new features, improvements, and bug-fixes in each release. Follow -links to the relevant GitHub issue or pull request for specific code changes -and any related discussion. - -.. include:: whats_new/v0.6.1.txt -.. include:: whats_new/v0.6.0.txt -.. include:: whats_new/v0.5.0.txt -.. include:: whats_new/v0.2.2.txt -.. include:: whats_new/v0.2.1.txt -.. include:: whats_new/v0.2.0.txt -.. include:: whats_new/v0.0.6.txt diff --git a/databroker/_static/basic.css b/databroker/_static/basic.css deleted file mode 100644 index 24a49f09b5..0000000000 --- a/databroker/_static/basic.css +++ /dev/null @@ -1,856 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a.brackets:before, -span.brackets > a:before{ - content: "["; -} - -a.brackets:after, -span.brackets > a:after { - content: "]"; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -dl.footnote > dt, -dl.citation > dt { - float: left; - margin-right: 0.5em; -} - -dl.footnote > dd, -dl.citation > dd { - margin-bottom: 0em; -} - -dl.footnote > dd:after, -dl.citation > dd:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dt:after { - content: ":"; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0.5em; - content: ":"; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -code.descclassname { - background-color: transparent; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/databroker/_static/bnl-styles.css b/databroker/_static/bnl-styles.css deleted file mode 100644 index 3e010e0ed8..0000000000 --- a/databroker/_static/bnl-styles.css +++ /dev/null @@ -1,8 +0,0 @@ -dl .jsonschematable{ - margin-left: 2em; - margin-top: 1em; -} - -dl .jsonschematable dl{ - margin-bottom: .75em; -} diff --git a/databroker/_static/css/badge_only.css b/databroker/_static/css/badge_only.css deleted file mode 100644 index e380325bc6..0000000000 --- a/databroker/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/databroker/_static/css/fonts/Roboto-Slab-Bold.woff b/databroker/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000018..0000000000 Binary files a/databroker/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/Roboto-Slab-Bold.woff2 b/databroker/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23142..0000000000 Binary files a/databroker/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/Roboto-Slab-Regular.woff b/databroker/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63f99..0000000000 Binary files a/databroker/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/Roboto-Slab-Regular.woff2 b/databroker/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5bda..0000000000 Binary files a/databroker/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/fontawesome-webfont.eot b/databroker/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca953..0000000000 Binary files a/databroker/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/databroker/_static/css/fonts/fontawesome-webfont.svg b/databroker/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845e53..0000000000 --- a/databroker/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/databroker/_static/css/fonts/fontawesome-webfont.ttf b/databroker/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2fa1..0000000000 Binary files a/databroker/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/databroker/_static/css/fonts/fontawesome-webfont.woff b/databroker/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a4b0..0000000000 Binary files a/databroker/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/fontawesome-webfont.woff2 b/databroker/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6040..0000000000 Binary files a/databroker/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-bold-italic.woff b/databroker/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b9ff..0000000000 Binary files a/databroker/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-bold-italic.woff2 b/databroker/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d804b5..0000000000 Binary files a/databroker/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-bold.woff b/databroker/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51f06..0000000000 Binary files a/databroker/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-bold.woff2 b/databroker/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb195043cf..0000000000 Binary files a/databroker/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-normal-italic.woff b/databroker/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc033..0000000000 Binary files a/databroker/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-normal-italic.woff2 b/databroker/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37e2e..0000000000 Binary files a/databroker/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-normal.woff b/databroker/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307ff5f..0000000000 Binary files a/databroker/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/databroker/_static/css/fonts/lato-normal.woff2 b/databroker/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843328..0000000000 Binary files a/databroker/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/databroker/_static/css/theme.css b/databroker/_static/css/theme.css deleted file mode 100644 index 8cd4f101a9..0000000000 --- a/databroker/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li span.toctree-expand:before,.wy-nav-top a,.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li span.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li span.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li span.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li span.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li span.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li span.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li span.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li span.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p.caption .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.btn .wy-menu-vertical li span.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p.caption .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.nav .wy-menu-vertical li span.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p.caption .btn .headerlink,.rst-content p.caption .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li span.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol li,.rst-content ol.arabic li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content ol.arabic li p:last-child,.rst-content ol.arabic li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.rst-content .wy-breadcrumbs li tt,.wy-breadcrumbs li .rst-content tt,.wy-breadcrumbs li code{padding:5px;border:none;background:none}.rst-content .wy-breadcrumbs li tt.literal,.wy-breadcrumbs li .rst-content tt.literal,.wy-breadcrumbs li code.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover span.toctree-expand,.wy-menu-vertical li.on a:hover span.toctree-expand{color:grey}.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand{display:block;font-size:.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover span.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover span.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content img{max-width:100%;height:auto}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp{user-select:none;pointer-events:none}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink{visibility:hidden;font-size:14px}.rst-content .code-block-caption .headerlink:after,.rst-content .toctree-wrapper>p.caption .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content table>caption .headerlink:after{content:"\f0c1";font-family:FontAwesome}.rst-content .code-block-caption:hover .headerlink:after,.rst-content .toctree-wrapper>p.caption:hover .headerlink:after,.rst-content dl dt:hover .headerlink:after,.rst-content h1:hover .headerlink:after,.rst-content h2:hover .headerlink:after,.rst-content h3:hover .headerlink:after,.rst-content h4:hover .headerlink:after,.rst-content h5:hover .headerlink:after,.rst-content h6:hover .headerlink:after,.rst-content p.caption:hover .headerlink:after,.rst-content table>caption:hover .headerlink:after{visibility:visible}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .hlist{width:100%}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl dt span.classifier:before{content:" : "}html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.field-list>dt:after,html.writer-html5 .rst-content dl.footnote>dt:after{content:":"}html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.footnote>dt>span.brackets{margin-right:.5rem}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{font-style:italic}html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.footnote>dd p,html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{font-size:inherit;line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dl:not(.field-list)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dl:not(.field-list)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code,html.writer-html4 .rst-content dl:not(.docutils) tt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/databroker/_static/doctools.js b/databroker/_static/doctools.js deleted file mode 100644 index 7d88f807dc..0000000000 --- a/databroker/_static/doctools.js +++ /dev/null @@ -1,316 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { - this.initOnKeyListeners(); - } - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keydown(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box, textarea, dropdown or button - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey - && !event.shiftKey) { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/databroker/_static/documentation_options.js b/databroker/_static/documentation_options.js deleted file mode 100644 index 43c2ef364b..0000000000 --- a/databroker/_static/documentation_options.js +++ /dev/null @@ -1,12 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '1.2.0b1', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false -}; \ No newline at end of file diff --git a/databroker/_static/file.png b/databroker/_static/file.png deleted file mode 100644 index a858a410e4..0000000000 Binary files a/databroker/_static/file.png and /dev/null differ diff --git a/databroker/_static/fonts/FontAwesome.otf b/databroker/_static/fonts/FontAwesome.otf deleted file mode 100644 index 401ec0f36e..0000000000 Binary files a/databroker/_static/fonts/FontAwesome.otf and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bold.eot b/databroker/_static/fonts/Lato/lato-bold.eot deleted file mode 100644 index 3361183a41..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bold.eot and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bold.ttf b/databroker/_static/fonts/Lato/lato-bold.ttf deleted file mode 100644 index 29f691d5ed..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bold.ttf and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bold.woff b/databroker/_static/fonts/Lato/lato-bold.woff deleted file mode 100644 index c6dff51f06..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bold.woff and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bold.woff2 b/databroker/_static/fonts/Lato/lato-bold.woff2 deleted file mode 100644 index bb195043cf..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bold.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bolditalic.eot b/databroker/_static/fonts/Lato/lato-bolditalic.eot deleted file mode 100644 index 3d4154936b..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bolditalic.eot and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bolditalic.ttf b/databroker/_static/fonts/Lato/lato-bolditalic.ttf deleted file mode 100644 index f402040b3e..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bolditalic.ttf and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bolditalic.woff b/databroker/_static/fonts/Lato/lato-bolditalic.woff deleted file mode 100644 index 88ad05b9ff..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bolditalic.woff and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-bolditalic.woff2 b/databroker/_static/fonts/Lato/lato-bolditalic.woff2 deleted file mode 100644 index c4e3d804b5..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-bolditalic.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-italic.eot b/databroker/_static/fonts/Lato/lato-italic.eot deleted file mode 100644 index 3f826421a1..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-italic.eot and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-italic.ttf b/databroker/_static/fonts/Lato/lato-italic.ttf deleted file mode 100644 index b4bfc9b24a..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-italic.ttf and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-italic.woff b/databroker/_static/fonts/Lato/lato-italic.woff deleted file mode 100644 index 76114bc033..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-italic.woff and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-italic.woff2 b/databroker/_static/fonts/Lato/lato-italic.woff2 deleted file mode 100644 index 3404f37e2e..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-italic.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-regular.eot b/databroker/_static/fonts/Lato/lato-regular.eot deleted file mode 100644 index 11e3f2a5f0..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-regular.eot and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-regular.ttf b/databroker/_static/fonts/Lato/lato-regular.ttf deleted file mode 100644 index 74decd9ebb..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-regular.ttf and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-regular.woff b/databroker/_static/fonts/Lato/lato-regular.woff deleted file mode 100644 index ae1307ff5f..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-regular.woff and /dev/null differ diff --git a/databroker/_static/fonts/Lato/lato-regular.woff2 b/databroker/_static/fonts/Lato/lato-regular.woff2 deleted file mode 100644 index 3bf9843328..0000000000 Binary files a/databroker/_static/fonts/Lato/lato-regular.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Bold.woff b/databroker/_static/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000018..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Bold.woff2 b/databroker/_static/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23142..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Light.woff b/databroker/_static/fonts/Roboto-Slab-Light.woff deleted file mode 100644 index 337d287116..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Light.woff and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Light.woff2 b/databroker/_static/fonts/Roboto-Slab-Light.woff2 deleted file mode 100644 index 20398aff31..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Light.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Regular.woff b/databroker/_static/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63f99..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Regular.woff2 b/databroker/_static/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5bda..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Thin.woff b/databroker/_static/fonts/Roboto-Slab-Thin.woff deleted file mode 100644 index 6b30ea630d..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Thin.woff and /dev/null differ diff --git a/databroker/_static/fonts/Roboto-Slab-Thin.woff2 b/databroker/_static/fonts/Roboto-Slab-Thin.woff2 deleted file mode 100644 index 328f5bb042..0000000000 Binary files a/databroker/_static/fonts/Roboto-Slab-Thin.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot deleted file mode 100644 index 79dc8efed3..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf deleted file mode 100644 index df5d1df273..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff deleted file mode 100644 index 6cb6000018..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 deleted file mode 100644 index 7059e23142..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot deleted file mode 100644 index 2f7ca78a1e..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf deleted file mode 100644 index eb52a79073..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff deleted file mode 100644 index f815f63f99..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff and /dev/null differ diff --git a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 deleted file mode 100644 index f2c76e5bda..0000000000 Binary files a/databroker/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/fontawesome-webfont.eot b/databroker/_static/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca953..0000000000 Binary files a/databroker/_static/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/databroker/_static/fonts/fontawesome-webfont.svg b/databroker/_static/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845e53..0000000000 --- a/databroker/_static/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/databroker/_static/fonts/fontawesome-webfont.ttf b/databroker/_static/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2fa1..0000000000 Binary files a/databroker/_static/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/databroker/_static/fonts/fontawesome-webfont.woff b/databroker/_static/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a4b0..0000000000 Binary files a/databroker/_static/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/databroker/_static/fonts/fontawesome-webfont.woff2 b/databroker/_static/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6040..0000000000 Binary files a/databroker/_static/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/lato-bold-italic.woff b/databroker/_static/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b9ff..0000000000 Binary files a/databroker/_static/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/databroker/_static/fonts/lato-bold-italic.woff2 b/databroker/_static/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d804b5..0000000000 Binary files a/databroker/_static/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/lato-bold.woff b/databroker/_static/fonts/lato-bold.woff deleted file mode 100644 index c6dff51f06..0000000000 Binary files a/databroker/_static/fonts/lato-bold.woff and /dev/null differ diff --git a/databroker/_static/fonts/lato-bold.woff2 b/databroker/_static/fonts/lato-bold.woff2 deleted file mode 100644 index bb195043cf..0000000000 Binary files a/databroker/_static/fonts/lato-bold.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/lato-normal-italic.woff b/databroker/_static/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc033..0000000000 Binary files a/databroker/_static/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/databroker/_static/fonts/lato-normal-italic.woff2 b/databroker/_static/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37e2e..0000000000 Binary files a/databroker/_static/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/databroker/_static/fonts/lato-normal.woff b/databroker/_static/fonts/lato-normal.woff deleted file mode 100644 index ae1307ff5f..0000000000 Binary files a/databroker/_static/fonts/lato-normal.woff and /dev/null differ diff --git a/databroker/_static/fonts/lato-normal.woff2 b/databroker/_static/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843328..0000000000 Binary files a/databroker/_static/fonts/lato-normal.woff2 and /dev/null differ diff --git a/databroker/_static/jquery-1.11.1.js b/databroker/_static/jquery-1.11.1.js deleted file mode 100644 index d4b67f7e6c..0000000000 --- a/databroker/_static/jquery-1.11.1.js +++ /dev/null @@ -1,10308 +0,0 @@ -/*! - * jQuery JavaScript Library v1.11.1 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-05-01T17:42Z - */ - -(function( global, factory ) { - - if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Can't do this because several apps including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// - -var deletedIds = []; - -var slice = deletedIds.slice; - -var concat = deletedIds.concat; - -var push = deletedIds.push; - -var indexOf = deletedIds.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var support = {}; - - - -var - version = "1.11.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android<4.1, IE<9 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num != null ? - - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - - // Return all the elements in a clean array - slice.call( this ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: deletedIds.sort, - splice: deletedIds.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var src, copyIsArray, copy, name, options, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - isWindow: function( obj ) { - /* jshint eqeqeq: false */ - return obj != null && obj == obj.window; - }, - - isNumeric: function( obj ) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - isPlainObject: function( obj ) { - var key; - - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Support: IE<9 - // Handle iteration over inherited properties before own properties. - if ( support.ownLast ) { - for ( key in obj ) { - return hasOwn.call( obj, key ); - } - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && jQuery.trim( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - // Support: Android<4.1, IE<9 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - var len; - - if ( arr ) { - if ( indexOf ) { - return indexOf.call( arr, elem, i ); - } - - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - while ( j < len ) { - first[ i++ ] = second[ j++ ]; - } - - // Support: IE<9 - // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) - if ( len !== len ) { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var args, proxy, tmp; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: function() { - return +( new Date() ); - }, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v1.10.19 - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-04-18 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + characterEncoding + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer - if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
"; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome<14 -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - - -var rneedsContext = jQuery.expr.match.needsContext; - -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; - }); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -}); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - init = jQuery.fn.init = function( selector, context ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : - // Execute immediately if ready is not present - selector( jQuery ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -jQuery.fn.extend({ - has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; - - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && cur.nodeType !== 1 ); - - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - ret = jQuery.unique( ret ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - } - - return this.pushStack( ret ); - }; -}); -var rnotwhite = (/\S+/g); - - - -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // First callback to fire (used internally by add and fireWith) - firingStart, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); - - } else if ( !(--remaining) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); - - -// The deferred used on DOM ready -var readyList; - -jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; -}; - -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); - -/** - * Clean-up method for dom ready events - */ -function detach() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - - } else { - document.detachEvent( "onreadystatechange", completed ); - window.detachEvent( "onload", completed ); - } -} - -/** - * The ready event handler and self cleanup method - */ -function completed() { - // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { - detach(); - jQuery.ready(); - } -} - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", completed ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", completed ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // detach all dom ready events - detach(); - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; - - -var strundefined = typeof undefined; - - - -// Support: IE<9 -// Iteration over object's inherited properties before its own -var i; -for ( i in jQuery( support ) ) { - break; -} -support.ownLast = i !== "0"; - -// Note: most support tests are defined in their respective modules. -// false until the test is run -support.inlineBlockNeedsLayout = false; - -// Execute ASAP in case we need to set body.style.zoom -jQuery(function() { - // Minified: var a,b,c,d - var val, div, body, container; - - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - // Return for frameset docs that don't have a body - return; - } - - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); - - if ( typeof div.style.zoom !== strundefined ) { - // Support: IE<8 - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; - - support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; - if ( val ) { - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - // Support: IE<8 - body.style.zoom = 1; - } - } - - body.removeChild( container ); -}); - - - - -(function() { - var div = document.createElement( "div" ); - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } - - // Null elements to avoid leaks in IE. - div = null; -})(); - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( elem ) { - var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], - nodeType = +elem.nodeType || 1; - - // Do not set data on non-element DOM nodes because it will not be cleared (#8335). - return nodeType !== 1 && nodeType !== 9 ? - false : - - // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute("classid") === noData; -}; - - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - -function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var ret, thisCache, - internalKey = jQuery.expando, - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - // Avoid exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( typeof name === "string" ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; -} - -function internalRemoveData( elem, name, pvt ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } else { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = name.concat( jQuery.map( name, jQuery.camelCase ) ); - } - - i = name.length; - while ( i-- ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - /* jshint eqeqeq: false */ - } else if ( support.deleteExpando || cache != cache.window ) { - /* jshint eqeqeq: true */ - delete cache[ id ]; - - // When all else fails, null - } else { - cache[ id ] = null; - } -} - -jQuery.extend({ - cache: {}, - - // The following elements (space-suffixed to avoid Object.prototype collisions) - // throw uncatchable exceptions if you attempt to set expando properties - noData: { - "applet ": true, - "embed ": true, - // ...but Flash objects (which have this classid) *can* handle expandos - "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data ) { - return internalData( elem, name, data ); - }, - - removeData: function( elem, name ) { - return internalRemoveData( elem, name ); - }, - - // For internal use only. - _data: function( elem, name, data ) { - return internalData( elem, name, data, true ); - }, - - _removeData: function( elem, name ) { - return internalRemoveData( elem, name, true ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[0], - attrs = elem && elem.attributes; - - // Special expections of .data basically thwart jQuery.access, - // so implement the relevant behavior ourselves - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE11+ - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - return arguments.length > 1 ? - - // Sets one value - this.each(function() { - jQuery.data( this, key, value ); - }) : - - // Gets one value - // Try to fetch any internally stored data first - elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - - -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHidden = function( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); - }; - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; -}; -var rcheckableType = (/^(?:checkbox|radio)$/i); - - - -(function() { - // Minified: var a,b,c - var input = document.createElement( "input" ), - div = document.createElement( "div" ), - fragment = document.createDocumentFragment(); - - // Setup - div.innerHTML = "
a"; - - // IE strips leading whitespace when .innerHTML is used - support.leadingWhitespace = div.firstChild.nodeType === 3; - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - support.tbody = !div.getElementsByTagName( "tbody" ).length; - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - support.html5Clone = - document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - input.type = "checkbox"; - input.checked = true; - fragment.appendChild( input ); - support.appendChecked = input.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE6-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // #11217 - WebKit loses check when the name is after the checked attribute - fragment.appendChild( div ); - div.innerHTML = ""; - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE<9 - // Opera does not clone events (and typeof div.attachEvent === undefined). - // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() - support.noCloneEvent = true; - if ( div.attachEvent ) { - div.attachEvent( "onclick", function() { - support.noCloneEvent = false; - }); - - div.cloneNode( true ).click(); - } - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } -})(); - - -(function() { - var i, eventName, - div = document.createElement( "div" ); - - // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) - for ( i in { submit: true, change: true, focusin: true }) { - eventName = "on" + i; - - if ( !(support[ i + "Bubbles" ] = eventName in window) ) { - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - div.setAttribute( eventName, "t" ); - support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; - } - } - - // Null elements to avoid leaks in IE. - div = null; -})(); - - -var rformElems = /^(?:input|select|textarea)$/i, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - var tmp, events, t, handleObjIn, - special, eventHandle, handleObj, - handlers, type, namespaces, origType, - elemData = jQuery._data( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - var j, handleObj, tmp, - origCount, t, events, - special, handlers, type, - namespaces, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - var handle, ontype, cur, - bubbleType, special, tmp, i, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - try { - elem[ type ](); - } catch ( e ) { - // IE<9 dies on focus/blur to hidden element (#1486,#12518) - // only reproducible on winXP IE8 native, not IE9 in IE8 mode - } - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, ret, handleObj, matched, j, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var sel, handleObj, matches, i, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - /* jshint eqeqeq: false */ - for ( ; cur != this; cur = cur.parentNode || this ) { - /* jshint eqeqeq: true */ - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: IE<9 - // Fix target property (#1925) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Support: Chrome 23+, Safari? - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Support: IE<9 - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) - event.metaKey = !!event.metaKey; - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var body, eventDoc, doc, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - try { - this.focus(); - return false; - } catch ( e ) { - // Support: IE<9 - // If we error on focus to hidden element (#1486, #12518), - // let .trigger() run the handlers - } - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; - - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === strundefined ) { - elem[ name ] = null; - } - - elem.detachEvent( name, handle ); - } - }; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - // Support: IE < 9, Android < 4.0 - src.returnValue === false ? - returnTrue : - returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - if ( !e ) { - return; - } - - // If preventDefault exists, run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // Support: IE - // Otherwise set the returnValue property of the original event to false - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - if ( !e ) { - return; - } - // If stopPropagation exists, run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - - // Support: IE - // Set the cancelBubble property of the original event to true - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// IE submit delegation -if ( !support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "submitBubbles" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "submitBubbles", true ); - } - }); - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "changeBubbles", true ); - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - jQuery._removeData( doc, fix ); - } else { - jQuery._data( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var type, origFn; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); - - -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
", "
" ], - area: [ 1, "", "" ], - param: [ 1, "", "" ], - thead: [ 1, "", "
" ], - tr: [ 2, "", "
" ], - col: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : - undefined; - - if ( !found ) { - for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); - } - } - } - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; -} - -// Used in buildFragment, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} - -// Support: IE<8 -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - if ( match ) { - elem.type = match[1]; - } else { - elem.removeAttribute("type"); - } - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; (elem = elems[i]) != null; i++ ) { - jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); - } -} - -function cloneCopyEvent( src, dest ) { - - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } - - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} - -function fixCloneNodeIssues( src, dest ) { - var nodeName, e, data; - - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } - - nodeName = dest.nodeName.toLowerCase(); - - // IE6-8 copies events bound via attachEvent when using cloneNode. - if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { - data = jQuery._data( dest ); - - for ( e in data.events ) { - jQuery.removeEvent( dest, e, data.handle ); - } - - // Event data gets referenced instead of copied if the expando gets copied too - dest.removeAttribute( jQuery.expando ); - } - - // IE blanks contents when cloning scripts, and tries to evaluate newly-set text - if ( nodeName === "script" && dest.text !== src.text ) { - disableScript( dest ).text = src.text; - restoreScript( dest ); - - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - } else if ( nodeName === "object" ) { - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } - - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { - dest.innerHTML = src.innerHTML; - } - - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.defaultSelected = dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, node, clone, i, srcElements, - inPage = jQuery.contains( elem.ownerDocument, elem ); - - if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); - - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } - - if ( (!support.noCloneEvent || !support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - // Fix all IE cloning issues - for ( i = 0; (node = srcElements[i]) != null; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - fixCloneNodeIssues( node, destElements[i] ); - } - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0; (node = srcElements[i]) != null; i++ ) { - cloneCopyEvent( node, destElements[i] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - destElements = srcElements = node = null; - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var j, elem, contains, - tmp, tag, tbody, wrap, - l = elems.length, - - // Ensure a safe fragment - safe = createSafeFragment( context ), - - nodes = [], - i = 0; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || safe.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; - - // Descend through wrappers to the right content - j = wrap[0]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Manually add leading whitespace removed by IE - if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); - } - - // Remove IE's autoinserted from table fragments - if ( !support.tbody ) { - - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : - - // String was a bare or - wrap[1] === "
" && !rtbody.test( elem ) ? - tmp : - 0; - - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { - elem.removeChild( tbody ); - } - } - } - - jQuery.merge( nodes, tmp.childNodes ); - - // Fix #12392 for WebKit and IE > 9 - tmp.textContent = ""; - - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); - } - - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; - } - } - } - - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); - } - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - tmp = null; - - return safe; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var elem, type, id, data, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = support.deleteExpando, - special = jQuery.event.special; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( acceptData || jQuery.acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { - - delete cache[ id ]; - - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( typeof elem.removeAttribute !== strundefined ) { - elem.removeAttribute( internalKey ); - - } else { - elem[ internalKey ] = null; - } - - deletedIds.push( id ); - } - } - } - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - } - - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - - // If this is a select, ensure that it displays empty (#12336) - // Support: IE<9 - if ( elem.options && jQuery.nodeName( elem, "select" ) ) { - elem.options.length = 0; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for (; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - elem = this[i] || {}; - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch(e) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var arg = arguments[ 0 ]; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; - - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var first, node, hasScripts, - scripts, doc, fragment, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[0], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[0] = value.call( this, index, self.html() ); - } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[i], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - } - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - i = 0, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone(true); - jQuery( insert[i] )[ original ]( elems ); - - // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - - -var iframe, - elemdisplay = {}; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "