<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 TIMESERIES WEB API
insert_ts(self, pk, ts)
Step8:  Having set up the triggers, now insert the time series, and upsert the metadata
==========================================
When it's first time to insert these keys in TSDB_server,
insert_ts will work and return TSDBStatus.OK
==========================================
<ASSISTANT_TASK:>
Python Code:
# server = subprocess.Popen(['python', '../go_persistent_server.py'])
# time.sleep(3)
# web = subprocess.Popen(['python', '../go_web.py'])
# time.sleep(3)
web_interface = WebInterface()
results = web_interface.add_trigger(
            'junk', 'insert_ts', None, 'db:one:ts')
assert results[0] == 200
print(results)
results = web_interface.add_trigger(
            'stats', 'insert_ts', ['mean', 'std'], None)
assert results[0] == 200
print(results[1])
def tsmaker(m, s, j):
    '''
    Helper function: randomly generates a time series for testing.
    Parameters
    ----------
    m : float
        Mean value for generating time series data
    s : float
        Standard deviation value for generating time series data
    j : float
        Quantifies the "jitter" to add to the time series data
    Returns
    -------
    A time series and associated meta data.
    '''
    # generate metadata
    meta = {}
    meta['order'] = int(np.random.choice(
        [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]))
    meta['blarg'] = int(np.random.choice([1, 2]))
    # generate time series data
    t = np.arange(0.0, 1.0, 0.01)
    v = norm.pdf(t, m, s) + j * np.random.randn(ts_length)
    # return time series and metadata
    return meta, TimeSeries(t, v)
mus = np.random.uniform(low=0.0, high=1.0, size=50)
sigs = np.random.uniform(low=0.05, high=0.4, size=50)
jits = np.random.uniform(low=0.05, high=0.2, size=50)
ts_length = 100
# initialize dictionaries for time series and their metadata
tsdict = {}
metadict = {}
for i, m, s, j in zip(range(50), mus, sigs, jits):
    meta, tsrs = tsmaker(m, s, j)
    # the primary key format is ts-1, ts-2, etc
    pk = "ts-{}".format(i)
    tsdict[pk] = tsrs
    meta['vp'] = False # augment metadata with a boolean asking if this is a  VP.
    metadict[pk] = meta
vpkeys = ["ts-{}".format(i) for i in np.random.choice(range(50), size=5, replace=False)]
for i in range(5):
    # add 5 triggers to upsert distances to these vantage points
    # data = json.dumps({'proc':'corr', 'onwhat':'insert_ts', 'target':["d_vp-{}".format(i)], 'arg':tsdict[vpkeys[i]].to_json()})
    # r = requests.post(self.web_url+'/add_trigger', data)
    r = web_interface.add_trigger('corr', 'insert_ts', ["d_vp-{}".format(i)], tsdict[vpkeys[i]].to_json())
    assert(r[0] == 200)
    # change the metadata for the vantage points to have meta['vp']=True
    metadict[vpkeys[i]]['vp'] = True
Explanation: TIMESERIES WEB API
insert_ts(self, pk, ts):
    
    Insert a timeseries into the database by sending a request to the server.
    Parameters
    ----------
    primary_key: int
        a unique identifier for the timeseries
    ts: a TimeSeries object
        the timeseries object intended to be inserted to database
    
delete_ts(self, pk):
Delete a timeseries from the database by sending a request to the server.
Parameters
----------
primary_key: int
    a unique identifier for the timeseries
upsert_meta(self, pk, md):
Upserting metadata into the timeseries in the database designated by the promary key by sending the server a request.
Parameters
----------
primary_key: int
    a unique identifier for the timeseries
metadata_dict: dict
    the metadata to upserted into the timeseries
select(self, md={}, fields=None, additional=None):
Selecting timeseries elements in the database that match the criteria
set in metadata_dict and return corresponding fields with additional
features.
Parameters
----------
metadata_dict: dict
    the selection criteria (filters)
    (Options : 'blarg', 'order')
fields: dict
    If not `None`, only these fields of the timeseries are returned.
    Otherwise, the timeseries are returned.
additional: dict
    additional computation to perform on the query matches before they're
    returned. Currently provide "sort_by" and "limit" functionality
augmented_select(self, proc, target, arg=None, md={}, additional=None):
Parameters
----------
proc : enum
    which of the modules in procs,
    or name of module in procs with coroutine main.
    (Options: 'corr', 'junk', 'stats')
target : array of fieldnames
    will be mapped to the array of results from the coroutine.
    If the target is None rather than a list of fields, we'll assume no upserting
arg : additional argument
    (ex : Timeseries object)
metadata_dict : dict
                store info for TimeSeries other than TimeSeries object itself
                (ex. vantage point is metadata_dict['ts-14']['vp']
additional : dict
            (Options: {"sort_by":"-order"})
Returns
-------
tsdb status &amp; payload
add_trigger(self, proc, onwhat, target, arg=None):
Send the server a request to add a trigger.
Parameters
----------
`proc` : enum
    which of the modules in procs,
    or name of module in procs with coroutine main.
    (Options: 'corr', 'junk', 'stats')
`onwhat` :
    which op is this trigger running on
    (ex : "insert_ts")
`target` : array of fieldnames
    will be mapped to the array of results from the coroutine.
    If the target is None rather than a list of fields, we'll assume no upserting
`arg` :
    additional argument
    (ex : Timeseries object)
remove_trigger(self, proc, onwhat, target=None):
Send the server a request to REMOVE a trigger.
Parameters
----------
`proc` : enum
    which of the modules in procs,
    or name of module in procs with coroutine main.
    (Options: 'corr', 'junk', 'stats')
`onwhat` :
    which op is this trigger running on
    (ex : "insert_ts")
`target` : array of fieldnames
    will be mapped to the array of results from the coroutine.
    If the target is None rather than a list of fields, we'll assume no upserting
Here in the notebook we use subprocess to launch our server, to see the output better, we strongly suggest to launch server directly in terminal whenever possible!
Below queries are just to offer you an sense of how our web api works, feel free to try more with the api description we proveded above!
End of explanation
for k in tsdict:
    results = web_interface.insert_ts(k, tsdict[k])
    assert results[0] == 200
    # upsert meta
    results = web_interface.upsert_meta(k, metadict[k])
    assert results[0] == 200
results = web_interface.add_trigger(
            'junk', 'insert_ts', None, 'db:one:ts')
results
# ==========================================
# However if it's not first time to insert these keys,
# insert_ts will return TSDBStatus.INVALID_KEY
# ==========================================
# pick a random pk
idx = np.random.choice(list(tsdict.keys()))
# check that the time series is there now
results = web_interface.select({"primary_key": idx})
assert results[0] == 200
# delete an existing time series
results = web_interface.delete_ts(idx)
assert results[0] == 200
# check that the time series is no longer there
results = web_interface.select({"md":{"order": 1}, "fields":["ts"], "additional":{"sort_by":"-order"}})
assert results[0] == 200
# add the time series back in
results = web_interface.insert_ts(idx, tsdict[idx])
assert results[0] == 200
Explanation: Having set up the triggers, now insert the time series, and upsert the metadata
==========================================
When it's first time to insert these keys in TSDB_server,
insert_ts will work and return TSDBStatus.OK
==========================================
End of explanation
<END_TASK>