# 1. Log in to get the token from a REST endpoint
You need a user in the Pipeline `STG` environment:
*username: some-username
password: some-password
e-mail: email@gmail.com*
Login can be done via POST request to the server:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/api/login/' \
--form 'username=some-username' \
--form 'password=some-password'
```
Output (example token):
`{"token":"1234567890abcdef"}`
You will use this token in the further requests to the GraphQL API.
# 2. Using the Pipeline GraphQL endpoint
`STG` environment GraphQL API endpoint: https://stg.jetstream.seeen.com/graphql/
## 0) What's available at the endpoint? (schema introspection - optional)
### List all available types
```graphql
{
__schema {
types {
name
}
}
}
```
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{\n __schema {\n types {\n name\n }\n }\n}"}'
```
### List fields in chosen type (e.g. PipelineExecutionType)
```graphql
{
__type(name: "PipelineExecutionType") {
name
fields {
name
type {
name
kind
}
}
}
}
```
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{\n __type(name: \"PipelineExecutionType\") {\n name\n fields {\n name\n type {\n name\n kind\n }\n }\n }\n}","variables":{}}'
```
More about introspection: https://graphql.org/learn/introspection/
## 1) Uploading video to the pipeline
This is the first needed step to have a video processed by the pipeline.
Example video URL: https://cs-backend-stg-storage.s3.amazonaws.com/public-media/videos/000042/video.mp4
### Upload the video
First, you need to send a direct video link to the Pipeline to start the download. Notice that the Authorization token needs to be placed in the headers. Query to the API (it should be in the request body):
```graphql
mutation VideoUploadMutation($input: VideoUploadMutationInput!) {
videoUpload(input: $input) {
video {
id
status
}
}
}
```
`variables = {"input": {"url": "https://cs-backend-stg-storage.s3.amazonaws.com/public-media/videos/000042/video.mp4"}}`
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/'
--header 'Content-Type: application/json'
--header 'Authorization: Token 1234567890abcdef'
--data-raw '{"query":"mutation VideoUploadMutation($input: VideoUploadMutationInput!) {\n videoUpload(input: $input) {\n video {\n id\n status\n }\n }\n}\n","variables":{"input":{"url":"https://cs-backend-stg-storage.s3.amazonaws.com/public-media/videos/000042/video.mp4"}}}'
```
Example response:
```json
{
"data": {
"videoUpload": {
"video": {
"id": "VmlkZW99999999ZZZZZZZZZZZZ",
"status": "IDLE"
}
}
}
}
```
Save the **video id** returned by this request - it will be needed in the next steps.
### Check the video upload status
The video is being uploaded now. You need the following query for upload status checking. Once the upload is done, it will be `COMPLETED`. Use the **video id** from the previous step.
```graphql
query VideoDownload($id: ID!){
videoDownload(id: $id) {
status
}
}
```
`variables = {"id": "VmlkZW99999999ZZZZZZZZZZZZ"}`
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query VideoDownload($id: ID!){\n videoDownload(id: $id) {\n status\n }\n}\n","variables":{"id":"VmlkZW9Eb3dubG9hZFR5cGU6NTky"}}'
```
Example response:
```json
{
"data": {
"videoDownload": {
"status": "COMPLETED"
}
}
}
```
## 2) Submitting video for Pipeline Execution
Pipeline Execution means performing all the analysis of the video.
### Get the newest Pipeline Version
Pipelines may have different names - we are using the one called ***"Internal Pipeline"***.
The given Pipeline definition may change over time (for e.g. new tasks can be added), then its version is updated. We normally want to use the newest available version of the Pipeline.
Querying the newest Pipeline Version of the "Internal Pipeline":
```graphql
query LatestPipelineVersion($pipelineName: String!) {
pipelineVersions(orderBy: "-versionNumber", pipelineName: $pipelineName, first: 1) {
edges {
node {
id
}
}
}
}
```
`variables = {"pipelineName": "Internal Pipeline"}`
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query LatestPipelineVersion($pipelineName: String!) {\n pipelineVersions(orderBy: \"-versionNumber\", pipelineName: $pipelineName, first: 1) {\n edges {\n node {\n id\n }\n }\n }\n}\n","variables":{"pipelineName":"Internal Pipeline"}}'
```
Example response:
```json
{
"data": {
"pipelineVersions": {
"edges": [
{
"node": {
"id": "UGlwZWDwfxpbmVWZXuVHlwZToyOQ=="
}
}
]
}
}
}
```
Save the **pipeline version id** for further steps.
### Start Pipeline Execution
We will need the saved **pipeline version id** and **video id**.
```graphql
mutation Execute($input: PipelineExecutionMutationInput!){
pipelineExecute(input: $input) {
pipelineExecution {
id
state
}
errors {
field
code
message
}
}
}
```
`variables = {"input": {"pipelineVersionId": "UGlwZWDwfxpbmVWZXuVHlwZToyOQ==", "videoId": "VmlkZW99999999ZZZZZZZZZZZZ"}}`
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"mutation Execute($input: PipelineExecutionMutationInput!){\n pipelineExecute(input: $input) {\n pipelineExecution {\n id\n state\n }\n errors {\n field\n code\n message\n }\n }\n}","variables":{"input":{"pipelineVersionId":"UGlwZWDwfxpbmVWZXuVHlwZToyOQ==","videoId":"VmlkZW99999999ZZZZZZZZZZZZ"}}}'
```
Example of a successfull response indicating start of the execution:
```json
{
"data": {
"pipelineExecute": {
"pipelineExecution": {
"id": "UGlwZWxpbmVFeGVjXXXXXXXXUeXBlOjYyNA==",
"state": "CREATED"
},
"errors": null
}
}
}
```
As you can see, we are querying the `errors` field also. Example of an unsuccessfull response:
```json
{
"data": {
"pipelineExecute": {
"pipelineExecution": null,
"errors": [
{
"field": "pipelineVersionId",
"code": "PIPELINE_VERSION_ID_DOES_NOT_EXIST",
"message": "Pipeline version with id XXXXXXXXXXXXX does not exist."
}
]
}
}
}
```
Pipeline Execution started. The processing will take some time. Save the **pipeline execution id** for further use.
### Checking Pipeline Execution state
Similarly as in the video upload you need to be checking for the Pipeline Execution state untill it's `COMPLETED`.
Query:
```graphql
query PipelineExecution($id: ID!){
pipelineExecution(id: $id) {
state
}
}
```
`variables = {"id": "UGlwZWxpbmVFeGVjXXXXXXXXUeXBlOjYyNA=="}`
Using cURL:
```bash
curl --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query PipelineExecution($id: ID!){\n pipelineExecution(id: $id) {\n state\n }\n}","variables":{"id":"UGlwZWxpbmVFeGVjXXXXXXXXUeXBlOjYyNA=="}}'
```
Response when still running:
```json
{
"data": {
"pipelineExecution": {
"state": "RUNNING"
}
}
}
```
Once the Pipeline Execution is `COMPLETED` you will be able to retrieve its results.
## 3) Retrieving Pipeline Execution results
### Annotations
Annotation is an object in the pipeline's database. It can come from different sources (ML classifiers output, Google Vision, Clarifai...) and it's a standard format for annotating videos in the pipeline.
It is possible to retrieve Annotations for the given Pipeline Execution - this way you will get the results of the given Pipeline Execution.
Basic Annotations query with all the available fields:
```graphql
query pipelineExecution($id: ID!) {
pipelineExecution(id: $id) {
annotations {
edges {
node {
name
timeOffset
score
boundingBox
annotationType
classType
trackId
}
}
}
}
}
```
`variables = {"id": "UGlwZWVjdXRpb25UeXBlOjYyNA=="}`
Using cURL:
```bash
curl --request POST 'https://stg-eai-pipeline.tg.10clouds.io/graphql/' \
--header 'Authorization: Token 1234567890abcdef' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!) {\n pipelineExecution(id: $id) {\n annotations {\n edges {\n node {\n name\n timeOffset\n score\n boundingBox\n annotationType\n classType\n trackId\n }\n }\n }\n }\n}\n# {\n# __type(name: \"PipelineExecutionType\") {\n# name\n# fields {\n# name\n# args {\n# name\n# }\n# }\n# }\n# }\n# {\n# __schema {\n# queryType {\n# fields {\n# name\n# description\n# }\n# }\n# }\n# }\n\n","variables":{"id":"UGlwZWVjdXRpb25UeXBlOjYyNA=="}}'
```
Part of an example response:
```json
{
"data": {
"pipelineExecution": {
"annotations": {
"edges": [
{
"node": {
"name": "Hispanic",
"timeOffset": 28695,
"score": 0.59122276,
"boundingBox": "BoundingBox(x=0.178125, y=0.13333333333333333, width=0.090625, height=0.19444444444444445)",
"annotationType": "OBJECT",
"classType": "FACE_ETHNICITY",
"trackId": 24
}
},
{
"node": {
"name": "technology",
"timeOffset": 28000,
"score": 0.83774865,
"boundingBox": null,
"annotationType": "TAG",
"classType": null,
"trackId": null
}
}
]
}
}
}
}
```
### a) Filtering by `timeOffsetRange`
You can filter Annotations by the `timeOffsetRange` (in miliseconds). It needs to be represented by a string with two integers separated by a comma, for e.g.: ``"0,1000"`` or ``"5000,10000"``.
Example query with `timeOffsetRange` filter:
```graphql
query pipelineExecution($id: ID!, $timeOffsetRange: String) {
pipelineExecution(id: $id) {
annotations(timeOffsetRange: $timeOffsetRange) {
edges {
node {
name
timeOffset
}
}
}
}
}
```
`variables = {"id": "UGlwZWVjdXRpb25UeXBlOjYyNA==", "timeOffsetRange": "0,200"}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $timeOffsetRange: String) {\n pipelineExecution(id: $id) {\n annotations(timeOffsetRange: $timeOffsetRange) {\n edges {\n node {\n name\n timeOffset\n }\n }\n }\n }\n}","variables":{"id":"UGlwZWVjdXRpb25UeXBlOjYyNA==","timeOffsetRange":"0,200"}}'
```
Will return Annotations with timeOffset between 0 and 200 (inclusive!) miliseconds.
As we see above, the type of `$timeOffsetRange` variable is `String`.
### b) Filtering by `annotationType`
Available Annotation Types:
```python
OBJECT = "O"
TAG = "T"
FACE_PROPERTIES = "F"
FACE_NAME = "N"
ENTITY = "E"
SOUND = "S"
```
Example query with filters:
```graphql
query pipelineExecution($id: ID!, $annotationType: String!) {
pipelineExecution(id: $id) {
annotations(annotationType: $annotationType) {
edges {
node {
name
timeOffset
}
}
}
}
}
```
`variables = {"id": "pipeline-execution-id", "annotationType": "T"}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $annotationType: String!) {\n pipelineExecution(id: $id) {\n annotations(annotationType: $annotationType) {\n edges {\n node {\n name\n timeOffset\n }\n }\n }\n }\n}","variables":{"id":"pipeline-execution-id","annotationType":"T"}}'
```
Will filter the "TAG" Annotations only.
### c) Filtering by `classType`
Available Class Types (only in Face Detection Annotations!):
```python
FACE_AGE = "A"
FACE_ETHNICITY = "E"
FACE_GENDER = "G"
FACE_EMOTION = "M"
```
Example query with filters:
```graphql
query pipelineExecution($id: ID!, $classType: String!) {
pipelineExecution(id: $id) {
annotations(classType: $classType) {
edges {
node {
name
timeOffset
}
}
}
}
}
```
`variables = {"id": "pipeline-execution-id", "classType": "G"}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $classType: String!) {\n pipelineExecution(id: $id) {\n annotations(classType: $classType) {\n edges {\n node {\n name\n timeOffset\n }\n }\n }\n }\n}","variables":{"id":"pipeline-execution-id","classType":"G"}}'
```
Will filter the "GENDER" Class Type Annotations only.
### d) Pagination
By default, first 20 Annotations are returned when querying this field. You can adjust pagination yourself with the filters described here.
In order to know if you've reached the last page of the results, you need to check the `pageInfo` in your requests. Query with `pageInfo` and part of the response:
```graphql
query pipelineExecution($id: ID!) {
pipelineExecution(id: $id) {
annotations {
edges {
node {
name
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
```
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!) {\n pipelineExecution(id: $id) {\n annotations {\n edges {\n node {\n name\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n}","variables":{"id":"your-pipeline-id"}}'
```
Part of the response - notice that the cursor starts at position 0 and ends at 20 (exclusive).
```json
{
"data": {
"pipelineExecution": {
"annotations": {
"edges": [
{
"node": {
"name": "monochrome photography",
}
},
// (...)
]
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "0",
"endCursor": "20"
}
}
}
}
}
```
If we wanted to get the next page of 20 results we would need to change the filter to: `annotations(first: 20, after: "19")`
**NOTE!** Indexing starts at 0, so Annotation with index 19 will be actually the 20th object in this dataset. To sum up, this filter will give you the *following* 20 Annotations *after* the first 20. Consider this as a second page of the results. For the *third* page you'd use: `annotations(first: 20, after: "39")`
---
Let's imagine that there are only 28 Annotations available. If we make a query asking for first 50 Annotations and check the `pageInfo` field, it will return us all the 28 Annotations and the `pageInfo` will look like this:
```json
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "0",
"endCursor": "28"
}
```
We can see that there is no next page available - this is the end of the results.
---
Further examples of queries with explanation:
- First 300 Annotations
```graphql
query pipelineExecution($id: ID!, $first: Int!) {
pipelineExecution(id: $id) {
annotations(first: $first) {
edges {
node {
name
}
}
}
}
}
```
`variables = {"id": "pipeline-execution-id", "first": 300}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $first: Int!) {\n pipelineExecution(id: $id) {\n annotations(first: $first) {\n edges {\n node {\n name\n }\n }\n }\n }\n}","variables":{"id":"pipeline-execution-id","first":300}}'
```
---
- `first: 20, after: "39"` -> 20 Annotations that come after Annotation with index 39 (= after 40th object in the dataset).
```graphql
query pipelineExecution($id: ID!, $first: Int!, $after: String!) {
pipelineExecution(id: $id) {
annotations(first: $first, after: $after) {
edges {
node {
name
}
}
}
}
}
```
`variables = {"id": "pipeline-execution-id", "first": 20, "after": "39"}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $first: Int!, $after: String!) {\n pipelineExecution(id: $id) {\n annotations(first: $first, after: $after) {\n edges {\n node {\n name\n }\n }\n }\n }\n}","variables":{"id":"pipeline-execution-id","first":20,"after":"39"}}'
```
### Transcription
Query for the video Transcription:
```graphql
query pipelineExecution($id: ID!) {
pipelineExecution(id: $id) {
transcriptions {
edges {
node {
text
}
}
}
}
}
```
`variables = {"id": "UGlwZWVjdXRpb25UeXBlOjYyNA=="}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!) {\n pipelineExecution(id: $id) {\n transcriptions {\n edges {\n node {\n text\n }\n }\n }\n }\n}","variables":{"id":"UGlwZWVjdXRpb25UeXBlOjYyNA=="}}'
```
Example result:
```json
{
"data": {
"pipelineExecution": {
"transcriptions": {
"edges": [
{
"node": {
"text": "we're happy to announce the G Channel mobile app. This app was made by enthusiasts for enthusiasts. It's got a lot of unique features like contests and polls. You can even make memes and remix your videos. But the best part about it is that you can upload your videos straight onto the GP Channel network right from your phone. We're opening the door to collaborate with you, our audience to make this the automotive social app out there. So what you waiting for? Go download the free app, share with your friends and be a part of the GT Channel network."
}
}
]
}
}
}
}
```
### TranscriptionTokens
Query for the video TranscriptionTokens with all the available fields:
```graphql
query pipelineExecution($id: ID!) {
pipelineExecution(id: $id) {
transcriptionTokens {
edges {
node {
token
score
startTimestamp
endTimestamp
tokenType
}
}
}
}
}
```
`variables = {"id": "UGlwZWVjdXRpb25UeXBlOjYyNA=="}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!) {\n pipelineExecution(id: $id) {\n transcriptionTokens {\n edges {\n node {\n token \n score \n startTimestamp\n endTimestamp\n tokenType\n }\n }\n }\n }\n}","variables":{"id":"UGlwZWVjdXRpb25UeXBlOjYyNA=="}}'
```
Example result:
```json
{
"data": {
"pipelineExecution": {
"transcriptionTokens": {
"edges": [
{
"node": {
"token": "we're",
"score": 0.9958,
"startTimestamp": 240,
"endTimestamp": 450,
"tokenType": "P"
}
},
{
"node": {
"token": "happy",
"score": 1.0,
"startTimestamp": 450,
"endTimestamp": 710,
"tokenType": "P"
}
},
{
"node": {
"token": "to",
"score": 1.0,
"startTimestamp": 710,
"endTimestamp": 810,
"tokenType": "P"
}
},
{
"node": {
"token": "announce",
"score": 1.0,
"startTimestamp": 810,
"endTimestamp": 1340,
"tokenType": "P"
}
},
// (...)
]
}
}
}
}
```
Available `tokenTypes`:
```python
PRONUNCIATION = "P"
PUNCTUATION = "U"
```
You can filter the TranscriptionTokens with `timeOffsetRange` similarly to how you filter Annotations. Check the Annotations section for more details. Here is a quick example of a query with variables:
```graphql
query pipelineExecution($id: ID!, $timeOffsetRange: String!) {
pipelineExecution(id: $id) {
transcriptionTokens(timeOffsetRange: $timeOffsetRange) {
edges {
node {
token
score
startTimestamp
endTimestamp
tokenType
}
}
}
}
}
```
`variables = {"id": "pipeline-execution-id", "timeOffsetRange": "0,900"}`
Using cURL:
```bash
curl --location --request POST 'https://stg.jetstream.seeen.com/graphql/' \
--header 'Authorization: Token 1234567890' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query pipelineExecution($id: ID!, $timeOffsetRange: String!) {\n pipelineExecution(id: $id) {\n transcriptionTokens(timeOffsetRange: $timeOffsetRange) {\n edges {\n node {\n token \n score \n startTimestamp\n endTimestamp\n tokenType\n }\n }\n }\n }\n}","variables":{"id":"pipeline-execution-id","timeOffsetRange":"0,900"}}'
```