Usage
Initializing the Client
To use the Watz API you need to obtain a key. See the API section on the Watz website for details.
Alternatively, a Client
can read the api key from the environment variable WATZ_SECRET
.
import os
import watz
os.environ["WATZ_SECRET"] = "your api key"
client = watz.Client()
Info
Each call made with the Client
is atomic. If something is invalid about the request, an exception will be raised and no changes will be made.
Creating Subjects
A subject is an individual whom data can be assigned to. These subjects can be identified by their uid
, currently always their email address.
Subjects are created with a NewSubject
model, and return a Subject
.
print(
client.subject_create(
[
watz.models.NewSubject(email="bar@bar.com"),
watz.models.NewSubject(email="foo@foo.com"),
]
)
)
"""
[
Subject(
uid="bar@bar.com",
email="bar@bar.com",
activities=[],
),
Subject(
uid="foo@foo.com",
email="foo@foo.com",
activities=[],
),
]
"""
Creating Activities
An activity is a specific event in time for a given subject, where data can be grouped together.
To create an activity, the only requirement is a subject_uid
of a previously created subject. Currently, the subject_uid
will always be the user's email address and can be accessed from subject.uid
of a Subject
model.
Activities are created with a NewActivity
model, and return an Activity
.
Activities can be passed some optional parameters too:
Parameter Name | Default | Description |
---|---|---|
label |
"No label" |
This label is attached to all Activity objects, and can help identify activities |
start_time |
datetime.datetime.now() |
This marks the start of the specific activity |
fit_files |
[] |
Fit files in the form of bytes will be parsed and used to automatically create traces for the activity |
import os
import datetime as dt
with open("fit_file.fit", "rb") as f:
fit_file = f.read()
print(
client.activity_create(
[
watz.models.NewActivity(subject_uid="bar@bar.com"),
watz.models.NewActivity(
subject_uid="foo@foo.com",
label="foo",
start_time=dt.datetime(2021, 1, 1),
fit_files=[fit_file],
),
]
)
)
"""
[
Activity(
uid="act_1_uid", label="No Label", start_time=dt.datetime.now()
),
Activity(
uid="act_2_uid", label="foo", start_time=dt.datetime(2021, 1, 1)
),
]
"""
Listing Subjects & Activities
The Client
can be used to list existing subjects and activities in the system.
print(client.subject_list())
"""
[
Subject(
uid="bar@bar.com",
email="bar@bar.com",
activities=[
Activity(
uid="act_1_uid",
label="No Label",
start_time=dt.datetime.now(),
)
],
),
Subject(
uid="foo@foo.com",
email="foo@foo.com",
activities=[
Activity(
uid="act_2_uid",
label="foo",
start_time=dt.datetime(2021, 1, 1),
)
],
),
]
"""
Creating Traces
A trace is a store of arbitrary, json-serializable data that are attached to a subject or activity parent.
Traces are identified by their name
and parser_id
, which must be unique for the given subject/activity parent.
Traces are created with a NewTrace
model, and return a Trace
.
Info
Whilst traces are stored as json, they can be passed some custom python types, which will be serialized to valid json.
E.g. datetime
, numpy.array([])
and pydantic models. The full list of data conversions can be found on the Conversions Page.
import datetime as dt
from pydantic import BaseModel
class ExamplePdModel(BaseModel):
a: int
b: str
print(
client.trace_create(
[
watz.models.NewTrace(
# Assigning to the subject "bar@bar.com"
parent_uid="bar@bar.com",
name="measurements",
data={
"height": 180,
"weight": 80,
},
),
watz.models.NewTrace(
# Assigning to the activity of the subject "foo@foo.com"
parent_uid="act_2_uid",
name="misc",
data=[
# Full list of supported types on Conversions Page
1,
"2",
True,
None,
["list"],
{"dict": "nested"},
(4, 5, 6),
dt.datetime(2021, 1, 1),
ExamplePdModel(a=11, b="12"),
],
),
]
)
)
"""
[
Trace(uid="123", name="measurements", parser_id=2),
Trace(uid="678", name="misc", parser_id=2),
]
"""
Info
parser_id
is implicit. It's used to identify how that data entered the system. For manual inputs, it's always 2
, for e.g. fit file generated traces, it's 3
.
Retrieving Traces
Trace information for a given list of parents can be pulled using client.trace_list()
.
Important
client.trace_list()
excludes the data itself, client.trace_hydrate()
is needed to retrieve the actual data.
print(
client.trace_list(["bar@bar.com", "foo@foo.com", "act_2_uid"])
)
"""
{
"bar@bar.com": [
Trace(uid="123", name="measurements", parser_id=2)
],
"foo@foo.com": [],
"act_2_uid": [
Trace(uid="678", name="misc", parser_id=2),
# Further traces will have come from the fit file
# passed into the activity creation in the previous example.
...
],
}
"""
client.trace_hydrate()
can then be used to convert Trace
models to TraceWithData
models.
client.trace_hydrate()
can be called with an arbitrary list of traces:
traces = client.trace_list(["bar@bar.com", "foo@foo.com", "1_act_uid"])
print(
client.trace_hydrate(traces["bar@bar.com"])
)
"""
[
TraceWithData(
uid="123",
name="measurements",
parser_id=2,
data={"height": 180, "weight": 80},
),
]
"""
Info
client.trace_hydrate()
under the hood uses client.trace_data()
, which is actually returning the raw data.
Hydration can also be called on the full output of client.trace_list()
:
traces = client.trace_list(["bar@bar.com", "foo@foo.com", "1_act_uid"])
print(
client.trace_hydrate(traces)
)
"""
{
"bar@bar.com": [
TraceWithData(
uid="123",
name="measurements",
parser_id=2,
data={"height": 180, "weight": 80},
)
],
"1_act_uid": [
TraceWithData(
uid="789",
name="activity_misc",
parser_id=2,
data=[
1,
'2',
True,
None,
['list'],
{'dict': 'nested'},
[4, 5, 6],
'2021-01-01T00:00:00+00:00',
{'a': 11, 'b': '12'}
],
)
],
}
"""
Tip
client.trace_hydrate()
output matches the input. If list[Trace]
is input, list[TraceWithData]
is returned, if dict[str, list[Trace]]
is passed in, dict[str, list[TraceWithData]]
is returned.
Tip
client.trace_list()
and client.trace_hydrate()
are separated to minify unwanted data transfer. The request might be rejected if the size of tranfer requested is too large, which is why it can be useful to target exactly what's needed.
Created: October 18, 2023