from seeq import spy
import pandas as pd

# Set the compatibility option so that you maximize the chance that SPy will remain compatible with your notebook/script
spy.options.compatibility = 193
# Log into Seeq Server if you're not using Seeq Data Lab:
spy.login(url='http://localhost:34216', credentials_file='../credentials.key', force=False)

spy.push

Uploads signals, conditions, scalars, and assets to the Seeq Server.

There are two main types of information processed by Seeq: Data and metadata:

  • Data is the time series and time interval information that is either collected or derived from sensor data. It consists of timestamps and values (samples), or time intervals and properties (capsules). This data can be plotted on a trend or used to train a neural network, for example.

  • Metadata is the information about the data, that is independent of a particular point in time or time interval. For example, a signal’s name, description and unit of measure is classified as metadata; or the formula that is used to derive a new signal from one or more source signals; or the asset tree that is used to model similar equipment or industrial processes.

The spy.push() command allows you to upload both types of information to Seeq Server. When you push metadata, you make entries in Seeq’s data index, which allows you or other users to search for and find such entries. If you also push data, then samples or capsules or scalars will appear when the user selects those index entries for inclusion on a trend, scatter plot, scorecard or other visualization.

spy.push(data=None, metadata=None, item_type=None, workbook='Data Lab >> Data Lab Analysis',
         worksheet='From Data Lab', datasource=None, archive=False, type_mismatches='raise', errors='catalog')

Workbook scoping

When you push any type of data, it is only available/discoverable within the workbook specified by the workbook argument. This allows you to sandbox your activity by default, and only publish to your colleagues later when your experimentation is largely over.

Pushing signal data

The simplest activity you can do with the spy.push() command is to read in a CSV file using Pandas and push it into Seeq. It will be stored in Seeq’s internal time series database.

import csv
csv_file = pd.read_csv('Support Files/csv_import_example.csv', parse_dates=['TIME(unitless)'], index_col='TIME(unitless)')
csv_file.head()

When you want to push data, it must have an index with a datetime data type. That’s why we used the parse_dates and index_col arguments for Pandas.read_csv().

Now you can just push it into Seeq:

push_results = spy.push(data=csv_file, workbook='SPy Documentation Examples >> spy.push')
push_results

NOTE: Pushing data can be relatively slow. This is an area that Seeq will be optimizing in future versions.

You can push multiple times, and as long as the names are the same and the workbook hasn’t changed, you’ll just add to the existing signal.

Pushing metadata

Now let’s try pushing just metadata. You can see that the column names from the CSV file contain the unit of measure in parentheses. Let’s use Pandas to extract the name and the unit of measure as separate columns.

better_metadata = push_results.copy()
better_metadata['Original Name'] = better_metadata.index
better_metadata['Name'] = better_metadata['Original Name'].str.extract(r'(.*)\(')
better_metadata['Value Unit Of Measure'] = better_metadata['Original Name'].str.extract(r'.*\((.*)\)')
better_metadata
spy.push(metadata=better_metadata, workbook='SPy Documentation Examples >> spy.push')

Pushing condition and capsule data

You can also push capsules, which are time intervals of interest, to Seeq by supplying a DataFrame with Capsule Start and Capsule End columns. Any additional columns will be added as properties of the capsule.

capsule_data = pd.DataFrame([{
    'Capsule Start':    pd.to_datetime('2019-01-10T09:00:00.000Z'),
    'Capsule End':      pd.to_datetime('2019-01-10T17:00:00.000Z'),
    'Operator On Duty': 'Mark'
}, {
    'Capsule Start':    pd.to_datetime('2019-01-11T09:00:00.000Z'),
    'Capsule End':      pd.to_datetime('2019-01-11T17:00:00.000Z'),
    'Operator On Duty': 'Hedwig'
}])

capsule_data

When you push capsule data, you must supply a metadata DataFrame that contains, at minimum, the Name, Type and Maximum Duration columns like the example below.

If your metadata DataFrame includes multiple conditions, the data DataFrame must have a Condition column to indicate which condition will receive that particular capsule. The value of the Condition column must correspond to an index entry of the metadata DataFrame.

spy.push(data=capsule_data,
         metadata=pd.DataFrame([{
             'Name': 'Operator Shifts',
             'Type': 'Condition',
             'Maximum Duration': '2d'
         }]), workbook='SPy Documentation Examples >> spy.push')

Capsule properties that have units of measure can be specified in the metadata. Say you have properties like Height and Mass:

capsule_data = pd.DataFrame([{
    'Capsule Start':    pd.to_datetime('2019-01-10T09:00:00.000Z'),
    'Capsule End':      pd.to_datetime('2019-01-10T17:00:00.000Z'),
    'Height': 5,
    'Mass': 10,
}, {
    'Capsule Start':    pd.to_datetime('2019-01-11T09:00:00.000Z'),
    'Capsule End':      pd.to_datetime('2019-01-11T17:00:00.000Z'),
    'Height': 3,
    'Mass' : 6
}])

capsule_data

Use Capsule Property Units in metadata dataframe to specify that Height has units of meters, m and Mass has units of kg :

spy.push(data=capsule_data,
         metadata=pd.DataFrame([{
             'Name': 'In Production',
             'Type': 'Condition',
             'Maximum Duration': '2d',
             'Capsule Property Units': {'Height': 'm',
                                        'Mass': 'kg'}
         }]), workbook='SPy Documentation Examples >> spy.push')

Updating capsules

To update capsules, you must replace them with new capsules. An easy workflow for this is to pull all the capsules in the range you want to edit, make changes, and then push with the replace argument set to the same range.

First, get the condition using spy.search():

condition = spy.search({'Name': 'In Production'}, workbook='SPy Documentation Examples >> spy.push')
condition

The start and end should be identical for both the spy.pull() and the replace argument to spy.push() so that nothing is accidentally deleted or duplicated. To make this easier, save them as variables.

replace_start=pd.to_datetime('2019-01-10T09:00:00.000Z')
replace_end=pd.to_datetime('2019-01-11T17:00:00.000Z')

Get all the capsules within that range:

new_capsule_data = spy.pull(condition,
                            start=replace_start,
                            end=replace_end)
new_capsule_data

Now we can update specific capsule properies. Let’s say the height of the first capsule was actually 4 meters and the Mass for the second capsule is 7 kg :

new_capsule_data.at[0, 'Height'] = 4
new_capsule_data.at[1, 'Mass'] = 7
new_capsule_data

Additional capsule propeties can also be added at this time. Suppose you want to record what the temperature was for the first capsule. A new Temperature property can be added:

new_capsule_data.at[0, 'Temperature'] = 67
new_capsule_data

You must specify units of measure for existing properties (e.g., Height and Mass) with every spy.push() along with units for new properties (e.g., Temperature measured in F).

The replace parameter takes a dictionary with the keys 'Start' and 'End'. Any capsules that start in the provided time period will be replaced.

Push the new_capsule_data using the replace parameter.

spy.push(data=new_capsule_data,
         metadata=pd.DataFrame([{
             'Name': 'In Production',
             'Type': 'Condition',
             'Maximum Duration': '2d',
             'Capsule Property Units': {'Height':'m',
                                        'Mass':'kg',
                                        'Temperature':'F'}
         }]),
         replace={'Start':   replace_start,
                  'End':     replace_end},
         workbook='SPy Documentation Examples >> spy.push')

Deleting capsules

The replace argument can also be used to delete capsules. All capsules starting within the time range specified by replace are deleted before the new capsules are added, so providing no new capsules to spy.push() functions as a simple deletion.

Caution: any capsules whose Start value is within the replace range will be deleted. The replace Start is inclusive and replace End is exclusive.

To delete the first capsule, we’ll specify a time range that includes its start (and no other capsule’s start):

spy.push(metadata=pd.DataFrame([{
             'Name': 'In Production',
             'Type': 'Condition',
             'Maximum Duration': '2d'}]),
         replace={'Start':    pd.to_datetime('2019-01-10T09:00:00.000Z'),
                  'End':      pd.to_datetime('2019-01-10T10:00:00.000Z')},
         workbook='SPy Documentation Examples >> spy.push')

If there are several capsules that start at the same time or within the same range, you can delete just one by following the update workflow but removing the capsule to be deleted from the pandas DataFrame before using spy.push()

Pushing calculated items

You can push Signals, Conditions and Scalars that are calculated via Seeq’s Formula language by supplying a metadata DataFrame with the following columns:

  • Formula: A string representing the Seeq Formula, written just as you would in the Formula tool within Seeq Workbench.

  • Formula Parameters: A dictionary where the keys represent variable names declared in the formula and the values can be:

    • A string that is the ID of the item.

    • A single-row DataFrame that contains an ID column referring to the item.

    • A (nested) dictionary of column/value pairs that reference another row’s cell(s) in the metadata DataFrame you are pushing. This is a forward reference, and SPy will handle the correct sequencing of item creation/updating. (You do not have to worry about the order in which items appear in the DataFrame.)

Let’s take a look at how all these methods work in practice:

import textwrap

# Search for the items to use as inputs
area_a_power_row = spy.search({
    'Datasource Name': 'Example Data',
    'Type': 'Signal',
    'Name': 'Area A_Compressor Power'
})

area_b_power_series = spy.search({
    'Datasource Name': 'Example Data',
    'Type': 'Signal',
    'Name': 'Area B_Compressor Power'
}).squeeze()

area_b_power_id = area_b_power_series['ID']

# Define a metadata DataFrame with two calculated items
metadata=pd.DataFrame([
    {
        'Name': 'Area A plus B Compressor Power',
        'Type': 'Signal',
        'Formula': textwrap.dedent("""
            $a + $b
        """).strip(),   # We use textwrap.dedent() and strip() on a multiline string to make it look good in the Formula tool
        'Formula Parameters': {
            '$a': area_a_power_row,
            '$b': area_b_power_id
        }
    }, {
        'Name': 'A and B High Inrush',
        'Type': 'Condition',
        'Formula': textwrap.dedent("""
            $AandB > 60
        """).strip(),
        'Formula Parameters': {
            '$AandB': {
                'Name': 'Area A plus B Compressor Power'   # You can also specify 'Path' and 'Asset' in the dictionary
            }
        }
    }])

# Push those calculations to the workbook
spy.push(metadata=metadata, workbook='SPy Documentation Examples >> spy.push')

As you can see in the results DataFrame, the A and B High Inrush Condition’s Formula Parameters refer (correctly) to the ID of Area A plus B Compressor Power.

Detailed Help

All SPy functions have detailed documentation to help you use them. Just execute help(spy.<func>) like you see below.

help(spy.push)