{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Command Reference\n",
    "\n",
    "The following sections provide in-depth reference for using the commands in the SPy module.\n",
    "\n",
    "[spy.login](spy.login.ipynb) - Logging into a Seeq Server with credentials.\n",
    "\n",
    "[spy.search](spy.search.ipynb) - Searching for signals, conditions and other items accessible in Seeq.\n",
    "\n",
    "[spy.pull](spy.pull.ipynb) - Retrieving data for a set of signals, conditions or scalars.\n",
    "\n",
    "[spy.push](spy.push.ipynb) - Uploading data for signals, conditions or scalars.\n",
    "\n",
    "[spy.swap](spy.swap.ipynb) - Swapping calculations across assets.\n",
    "\n",
    "[spy.workbooks](spy.workbooks.ipynb) - Pulling, modifying and pushing Workbench Analyses and Organizer Topics.\n",
    "\n",
    "[spy.jobs](spy.jobs.ipynb) - Scheduling notebooks to run in the background within Seeq Data Lab.\n",
    "\n",
    "[spy.acl](spy.acl.ipynb) - Retrieving/modifying Access Control Lists on items.\n",
    "\n",
    "## Asset Trees\n",
    "\n",
    "Representing your assets and processes in Seeq as Asset Trees is an important task that SPy can help with.\n",
    "The following sections walk you through the extensive functionality in this area:\n",
    "\n",
    "[Asset Trees 1 - Introduction](Asset%20Trees%201%20-%20Introduction.ipynb)\n",
    "\n",
    "[Asset Trees 2 - Templates](Asset%20Trees%202%20-%20Templates.ipynb)\n",
    "\n",
    "[Asset Trees 3 - Report and Dashboard Templates](Asset%20Trees%203%20-%20Report%20and%20Dashboard%20Templates.ipynb)\n",
    "\n",
    "[Asset Trees 4 - Accelerator Templates](Asset%20Trees%204%20-%20Accelerator%20Templates.ipynb)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Detailed Help\n",
    "\n",
    "In addition to the documentation notebooks mentioned in **Command Reference** above, all SPy functions \n",
    "include detailed help that you can access via Python's built-in `help()` function like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function search in module seeq.spy._search:\n",
      "\n",
      "search(query, *, all_properties=False, include_properties: 'List[str]' = None, workbook: 'Optional[str]' = 'Data Lab >> Data Lab Analysis', recursive: 'bool' = True, ignore_unindexed_properties: 'bool' = True, include_archived: 'bool' = False, include_swappable_assets: 'bool' = False, estimate_sample_period: 'Optional[dict]' = None, old_asset_format: 'bool' = None, order_by: 'Union[str, List[str]]' = None, limit: 'Optional[int]' = -1, errors: 'str' = None, quiet: 'bool' = None, status: 'Status' = None, session: 'Optional[Session]' = None) -> 'pd.DataFrame'\n",
      "    Issues a query to the Seeq Server to retrieve metadata for signals,\n",
      "    conditions, scalars and assets. This metadata can then be used to retrieve\n",
      "    samples, capsules for a particular time range via spy.pull().\n",
      "    \n",
      "    Parameters\n",
      "    ----------\n",
      "    query : {str, dict, list, pd.DataFrame, pd.Series}\n",
      "        A mapping of property / match-criteria pairs or a Seeq Workbench URL\n",
      "    \n",
      "        If you supply a dict or list of dicts, then the matching\n",
      "        operations are \"contains\" (instead of \"equal to\").\n",
      "    \n",
      "        If you supply a DataFrame or a Series, then the matching\n",
      "        operations are \"equal to\" (instead of \"contains\").\n",
      "    \n",
      "        If you supply a str, it must be the URL of a Seeq Workbench worksheet.\n",
      "        The retrieved metadata will be the signals, conditions and scalars\n",
      "        currently present on the Details Panel.\n",
      "    \n",
      "        'Name' and 'Description' fields support wildcard and regular expression\n",
      "        (regex) matching with the same syntax as within the Data tab in Seeq\n",
      "        Workbench.\n",
      "    \n",
      "        The 'Path' field allows you to query within an asset tree, where >>\n",
      "        separates each level from the next. E.g.: 'Path': 'Example >> Cooling*'\n",
      "        You can use wildcard and regular expression matching at any level but,\n",
      "        unlike the Name/Description fields, the match must be a \"full match\",\n",
      "        meaning that 'Path': 'Example' will match on a root asset tree node of\n",
      "        'Example' but not 'Example (AF)'.\n",
      "    \n",
      "        Available options are:\n",
      "    \n",
      "        =================== ===================================================\n",
      "        Property            Description\n",
      "        =================== ===================================================\n",
      "        Name                Name of the item (wildcards/regex supported)\n",
      "        Path                Asset tree path of the item (should not include the\n",
      "                            \"leaf\" asset), using ' >> ' hierarchy delimiters\n",
      "        Asset               Asset name (i.e., the name of the leaf asset) or ID\n",
      "        Type                The item type. One of 'Signal', 'Condition',\n",
      "                            'Scalar', 'Asset', 'Chart', 'Metric', 'Workbook',\n",
      "                            'Worksheet', and 'Display'\n",
      "        ID                  The item ID. If specified, all other properties\n",
      "                            are ignored and the item is retrieved directly. If\n",
      "                            the item ID is not found, an error will be raised.\n",
      "        Description         Description of the item (wildcards/regex supported)\n",
      "        Datasource Name     Name of the datasource\n",
      "        Datasource ID       The datasource ID, which corresponds to the Id\n",
      "                            field in the connector configuration\n",
      "        Datasource Class    The datasource class (e.g. 'OSIsoft PI')\n",
      "        Data ID             The data ID, whose format is managed by the\n",
      "                            datasource connector\n",
      "        Cache Enabled       True to find items where data caching is enabled\n",
      "        Scoped To           The Seeq ID of a workbook such that results are\n",
      "                            limited to ONLY items scoped to that workbook.\n",
      "        =================== ===================================================\n",
      "    \n",
      "    all_properties : bool, default False\n",
      "        Return all item properties in the result. If you would like to specify\n",
      "        exactly which properties to return (for better performance/less data),\n",
      "        use include_properties instead. If both all_properties and\n",
      "        include_properties are omitted, you will get only the properties that\n",
      "        come \"for free\" (no performance penalty) with the query.\n",
      "    \n",
      "    include_properties : list, optional\n",
      "        A list of extra properties to include in the results. If omitted, the\n",
      "        default set of properties will be returned. If both all_properties and\n",
      "        include_properties are omitted, you will get only the properties that\n",
      "        come \"for free\" (no performance penalty) with the query.\n",
      "    \n",
      "    workbook : {str, None}, default 'Data Lab >> Data Lab Analysis'\n",
      "        A path string (with ' >> ' delimiters) or an ID to indicate a workbook\n",
      "        such that, in addition to globally-scoped items, the workbook's scoped\n",
      "        items will also be returned in the results.\n",
      "    \n",
      "        If you want all items regardless of scope, use\n",
      "        workbook=spy.GLOBALS_AND_ALL_WORKBOOKS\n",
      "    \n",
      "        If you want only globally-scoped items, use\n",
      "        workbook=spy.GLOBALS_ONLY\n",
      "    \n",
      "        If you don't want globally-scoped items in your results, use the\n",
      "        'Scoped To' field in the 'query' argument instead. (See 'query'\n",
      "        argument documentation above.)\n",
      "    \n",
      "        The ID for a workbook is visible in the URL of Seeq Workbench, directly\n",
      "        after the \"workbook/\" part.\n",
      "    \n",
      "    recursive : bool, default True\n",
      "        If True, searches that include a Path entry will include items at and\n",
      "        below the specified location in an asset tree. If False, then only\n",
      "        items at the specified level will be returned. To get only the root\n",
      "        assets, supply a Path value of ''.\n",
      "    \n",
      "    ignore_unindexed_properties : bool, default True\n",
      "        If False, a ValueError will be raised if any properties are supplied\n",
      "        that cannot be used in the search.\n",
      "    \n",
      "    include_archived : bool, default False\n",
      "        If True, includes trashed/archived items in the output.\n",
      "    \n",
      "    include_swappable_assets : bool, default False\n",
      "        Adds a \"Swappable Assets\" column to the output where each cell is an\n",
      "        embedded DataFrame that includes the assets that the item refers to and\n",
      "        can theoretically be swapped for other assets using spy.swap().\n",
      "    \n",
      "    estimate_sample_period : dict, default None\n",
      "        A dict with the keys 'Start' and 'End'. If provided, an estimated\n",
      "        sample period for all signals will be included in the output. The\n",
      "        values for the 'Start' and 'End' keys must be a string that\n",
      "        pandas.to_datetime() can parse, or a pandas.Timestamp. The start\n",
      "        and end times are used to bound the calculation of the sample period.\n",
      "        If the start and end times encompass a time range that is insufficient\n",
      "        to determine the sample period, a pd.NaT will be returned.\n",
      "        If the value of 'Start' is set to None, it will default to the value\n",
      "        of 'End' minus 1 hour. Conversely, if the value of 'End' is set to\n",
      "        None, it will default to now.\n",
      "    \n",
      "    old_asset_format : bool, default True\n",
      "        Historically, spy.search() returned rows with a \"Type\" of \"Asset\"\n",
      "        whereby the \"Asset\" column was the name of the parent asset. This is\n",
      "        inconsistent with all other aspects of SPy, including\n",
      "        spy.push(metadata). If you would like Asset rows to instead be\n",
      "        consistent with the rest of SPy (whereby the \"Asset\" column is the\n",
      "        name of the current asset, not the parent), pass in False for this\n",
      "        argument.\n",
      "    \n",
      "    order_by : {str, list}, default None\n",
      "        An optional field or list of fields used to sort the search results.\n",
      "        Fields on which results can be sorted are 'ID', 'Name', and\n",
      "        'Description'.\n",
      "    \n",
      "    limit : int, default 1000\n",
      "        A limit on the number of results returned. By default, the limit is\n",
      "        1000. Specify limit=None to return all results.\n",
      "    \n",
      "    quiet : bool, default False\n",
      "        If True, suppresses progress output. Note that when status is\n",
      "        provided, the quiet setting of the Status object that is passed\n",
      "        in takes precedence.\n",
      "    \n",
      "    errors : {'raise', 'catalog'}, default 'raise'\n",
      "        If 'raise', any errors encountered will cause an exception. If\n",
      "        'catalog', errors will be added to a 'Result' column in the status.df\n",
      "        DataFrame.\n",
      "    \n",
      "    status : spy.Status, optional\n",
      "        If specified, the supplied Status object will be updated as the command\n",
      "        progresses. It gets filled in with the same information you would see\n",
      "        in Jupyter in the blue/green/red table below your code while the\n",
      "        command is executed. The table itself is accessible as a DataFrame via\n",
      "        the status.df property.\n",
      "    \n",
      "    session : spy.Session, optional\n",
      "        If supplied, the Session object (and its Options) will be used to\n",
      "        store the login session state. This is useful to log in to different\n",
      "        Seeq servers at the same time or with different credentials.\n",
      "    \n",
      "    Returns\n",
      "    -------\n",
      "    pandas.DataFrame\n",
      "        A DataFrame with rows for each item found and columns for each\n",
      "        property.\n",
      "    \n",
      "        Additionally, the following properties are stored on the \"spy\"\n",
      "        attribute of the output DataFrame:\n",
      "    \n",
      "        =================== ===================================================\n",
      "        Property            Description\n",
      "        =================== ===================================================\n",
      "        func                A str value of 'spy.search'\n",
      "        kwargs              A dict with the values of the input parameters\n",
      "                            passed to spy.search to get the output DataFrame\n",
      "        old_asset_format    True if the old Asset format was used (see doc for\n",
      "                            old_asset_format argument)\n",
      "        status              A spy.Status object with the status of the\n",
      "                            spy.search call\n",
      "        =================== ===================================================\n",
      "    \n",
      "    Examples\n",
      "    --------\n",
      "    Search for signals with the name 'Humid' on the asset tree under\n",
      "    'Example >> Cooling Tower 1', retrieving all properties on the results:\n",
      "    \n",
      "    >>> search_results = spy.search({'Name': 'Humid', 'Path': 'Example >> Cooling Tower 1'}, all_properties=True)\n",
      "    \n",
      "    To access the stored properties:\n",
      "    >>> search_results.spy.kwargs\n",
      "    >>> search_results.spy.status\n",
      "    \n",
      "    Search for signals that have a name that starts with 'Area' in the\n",
      "    datasource 'Example Data' and determine the sample period of each signal\n",
      "    during the month of January 2018\n",
      "    \n",
      "    >>> search_results = spy.search({\n",
      "    >>>    'Name': 'Area ?_*',\n",
      "    >>>    'Datasource Name': 'Example Data'\n",
      "    >>> }, estimate_sample_period=dict(Start='2018-01-01', End='2018-02-01'))\n",
      "    \n",
      "    Using a pandas.DataFrame as the input:\n",
      "    \n",
      "    >>> my_items = pd.DataFrame(\n",
      "    >>>     {'Name': ['Area A_Temperature', 'Area B_Compressor Power', 'Optimize' ],\n",
      "    >>>      'Datasource Name': 'Example Data'})\n",
      "    >>> spy.search(my_items)\n",
      "    \n",
      "    Using a URL from a Seeq Workbench worksheet:\n",
      "    \n",
      "    >>> my_worksheet_items = spy.search(\n",
      "    >>> 'https://seeq.com/workbook/17F31703-F0B6-4C8E-B7FD-E20897BD4819/worksheet/CE6A0B92-EE00-45FC-9EB3-D162632DBB48')\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from seeq import spy\n",
    "help(spy.search)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you are in the Jupyter user interface (e.g., you are using Seeq Data Lab), you can also access help by\n",
    "putting your cursor on the name of the function (e.g., the `search` part of `spy.search()`) and pressing\n",
    "Shift+Tab. (You may also need to click the `+` button to expand the view, since much of the SPy documentation is\n",
    "lengthy.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sessions\n",
    "\n",
    "When you use any SPy function that interacts with the Seeq service, you are implicitly using the default _session_. The session contains login information and serves as the means of authentication to the Seeq service.\n",
    "\n",
    "You can choose to manage the session explicitly by creating your own. This is most useful if you need to access and interact with multiple Seeq services in the same script / notebook.\n",
    "\n",
    "You can create a `spy.Session` object and then pass it in to all of the functions you use. For example:\n",
    "\n",
    "```\n",
    "# Create a session object\n",
    "session = spy.Session\n",
    "\n",
    "# Log in using the session object\n",
    "spy.login(url='https://mycompany.seeq.site', username='myname', password='mypassword', session=session)\n",
    "\n",
    "# Do something interesting using the session\n",
    "spy.search({'Name': '*mytag*12'}, session=session)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Options\n",
    "\n",
    "There are several options tied to your session that you can change. They all have sensible defaults, so make sure you read the documentation carefully before changing them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Assign a new value to the following variables if you would like to adjust them.\n",
      "\n",
      "E.g.:\n",
      "   spy.options.concurrent_requests = 3\n",
      "\n",
      "Available Options\n",
      "-----------------\n",
      "\n",
      "spy.options.compatibility (default: None)\n",
      "\n",
      "    The major version of SPy to emulate from a compatibility standpoint. This\n",
      "    is important to set if you would like to minimize the chance that your\n",
      "    script or add-on \"breaks\" when SPy is upgraded. Set it to the major version\n",
      "    of SPy that you have tested against. E.g.: spy.options.compatibility = 184\n",
      "\n",
      "spy.options.search_page_size (default: 1000)\n",
      "\n",
      "    The number of items retrieved on each round-trip to the Seeq Server during\n",
      "    a spy.search() call. If you have a fast system and fast connection, you can\n",
      "    make this higher.\n",
      "\n",
      "spy.options.pull_page_size (default: 1000000)\n",
      "\n",
      "    The number of samples/capsules retrieved on each round-trip to the Seeq\n",
      "    Server during a spy.pull() call. If you have a slow system or slow\n",
      "    connection, you may wish to make this lower. It is not recommended to\n",
      "    exceed 1000000.\n",
      "\n",
      "spy.options.push_page_size (default: 100000)\n",
      "\n",
      "    The number of samples/capsules uploaded during each round-trip to the Seeq\n",
      "    Server during a spy.push() call. If you have a slow system or slow\n",
      "    connection, you may wish to make this lower. It is not recommended to\n",
      "    exceed 1000000.\n",
      "\n",
      "spy.options.metadata_push_batch_size (default: 1000)\n",
      "\n",
      "    The number of items uploaded during each round-trip to the Seeq\n",
      "    Server during a spy.push(metadata) call. If you have a low-memory system\n",
      "    you may wish to make this lower. It is not recommended to exceed 10000.\n",
      "\n",
      "spy.options.max_concurrent_requests (default: 8)\n",
      "\n",
      "    The maximum number of simultaneous requests made to the Seeq Server during\n",
      "    spy.pull() and spy.push() calls. The higher the number, the more you can\n",
      "    monopolize the Seeq Server. If you keep it low, then other users are less\n",
      "    likely to be impacted by your activity.\n",
      "\n",
      "spy.options.retry_timeout_in_seconds (default: 5)\n",
      "\n",
      "    The amount of time to spend retrying a failed Seeq Server API call in an\n",
      "    attempt to overcome network flakiness.\n",
      "\n",
      "spy.options.request_timeout_in_seconds (default: None)\n",
      "\n",
      "    The amount of time to wait for a single request to complete, after which\n",
      "    the http client will consider it is taking too long and give up on it.\n",
      "    The default of None indicates there is no limit (infinite timeout).\n",
      "\n",
      "    This option may require an upgrade of the `seeq` Python package to\n",
      "    function - a warning will be provided if so.\n",
      "\n",
      "spy.options.clear_content_cache_before_render (default: False)\n",
      "\n",
      "    When using spy.workbooks.pull(include_rendered_content=True), always\n",
      "    re-render the content even if it had been previously rendered and cached.\n",
      "\n",
      "spy.options.force_calculated_scalars (default: True)\n",
      "\n",
      "    During spy.push(metadata), always push CalculatedScalars even if\n",
      "    LiteralScalars would normally apply. (Ignored in R60 and earlier.)\n",
      "\n",
      "spy.options.allow_version_mismatch (default: False)\n",
      "\n",
      "    Allow a major version mismatch between SPy and Seeq Server. (Normally,\n",
      "    a mismatch raises a RuntimeError.)\n",
      "\n",
      "spy.options.friendly_exceptions (default: True if running in Data Lab, otherwise False)\n",
      "\n",
      "    If True, exceptions raised in a Jupyter notebook will be displayed in a\n",
      "    friendlier format. Stack traces will not be shown by default for most\n",
      "    errors; error messages will precede the stack trace; and internal SPy\n",
      "    code will be omitted from the stack trace.\n",
      "\n",
      "spy.options.default_timezone (default: None)\n",
      "\n",
      "    If set to a timezone, this will be understood as the intended timezone\n",
      "    for all naive datetimes passed as input to SPy. This will not override\n",
      "    the timezone of any timezone-aware datetime. If set to None, naive\n",
      "    datetimes will be interpreted as being in the logged-in user's preferred\n",
      "    timezone. Timezone can be specified as str, pytz.timezone or dateutil.tzinfo.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from seeq import spy\n",
    "spy.options.help()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DataFrame Output Properties\n",
    "\n",
    "Many SPy functions return a DataFrame, and that DataFrame often has a special `spy` attribute on it that includes\n",
    "information about the provenance of the DataFrame.\n",
    "\n",
    "For example, if you assigned the results of `spy.search()` to a variable you can use the `spy` attribute to\n",
    "access the following properties of the original search:\n",
    "\n",
    "Property         |   Description\n",
    "-----------------|---------------------------------------------------------------------------------------------------\n",
    "func             |   A str value of 'spy.search'\n",
    "kwargs           |   A dict with the values of the input parameters passed to spy.search to get the output DataFrame \n",
    "status           |   A spy.Status object with the status of the spy.search call\n",
    "\n",
    "You can discover which properties are returned for a particular call by printing the detailed help (see below)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.11",
   "language": "python",
   "name": "python311"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
