# Tabroom REST API

An API for querying reliable debate data.

## About

[Tabroom.com](https://tabroom.com) is a service provided by the [National Speech and Debate Association](https://www.speechanddebate.org/) designed to host debate tournaments. [Tournaments.Tech](https://tourrnaments.tech) is a ranking site & database for debate which is built off [@http-samc/Tabroom-API](https://github.com/http-samc/tabroom-API), a locally-run set of open-sourced scraping utilities for Tabroom. If you want the docs for this, the (non-REST) Tabroom API, you can find them [here](https://github.com/http-samc/tabroom-API/blob/main/DOCS.md). The non-REST, locally ran Python API is ideal for those looking to use data from tournaments on Tabroom, but not scraped by Tournaments.Tech.

### Terms of Service

Tabroom-API and Tournaments.Tech, and the Tabroom REST API (this service) were created by Samarth Chitgopekar. They are provided to you under the terms of the MIT License, which is given below:

```
Copyright 2022 Samarth Chitgopekar

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```

### Getting Started

This resource includes documentation and examples. We recommend you start off by reading the documentation (it's very brief, there are only 3 endpoints) and then play around with examples.


# Documentation

This page contains all available endpoints for the Tabroom REST API and their specificaiton.

## Query Debaters

<mark style="color:blue;">`GET`</mark> `URL: https://tournaments.tech/query`

Search for a debater/entry within a specified format, year, and circuit.

#### Query Parameters

| Name                                      | Type   | Description                                                                                                                                                                 |
| ----------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| format<mark style="color:red;">\*</mark>  | String | A recognized debate format (see API Constants & Schemas for more).                                                                                                          |
| circuit<mark style="color:red;">\*</mark> | String | A recognized debate circuit for the specified format (see API Constants & Schemas for more).                                                                                |
| year<mark style="color:red;">\*</mark>    | String | A recognized debate year for the specified circuit (see API Constants & Schemas for more).                                                                                  |
| team                                      | String | An ID (see API Constants & Schemas for more) of a specific team that exists on the supplied format, circuit, and year. If provided, you'll only recieve data for that team. |
| term                                      | String | The term to query against the specified format, circuit, and year. Can be any combination of the entry's full names or any of the entry's codes.                            |

{% tabs %}
{% tab title="400: Bad Request Sent if you fail to provide valid information for all required query parameters or if you provide both a term and a team or neither a term or team." %}

```javascript
Invalid Query
```

{% endtab %}

{% tab title="500: Internal Server Error Sent when an unexpected error is encountered. A message is provided that includes what went wrong." %}

```javascript
{
    "message": <(String) error message>
}
```

{% endtab %}

{% tab title="200: OK An array of teams is returned if you queried using term." %}

```javascript
[<(Schemas.Team)>...]
```

{% endtab %}
{% endtabs %}

## Stable URL For Debaters

<mark style="color:blue;">`GET`</mark> `URL: https://tournaments.tech/stable/:fullNames`

A stable URL that can be safely linked in College Apps, etc. which will redirect to the team's latest page for the specified format, circuit, and year.

#### Path Parameters

| Name                                        | Type   | Description                                               |
| ------------------------------------------- | ------ | --------------------------------------------------------- |
| fullNames<mark style="color:red;">\*</mark> | String | The full names (URL safe) of the debater(s) of the entry. |

#### Query Parameters

| Name                                      | Type    | Description                                                                                                                                    |
| ----------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| format<mark style="color:red;">\*</mark>  | String  | A recognized debate format (see API Constants & Schemas for more).                                                                             |
| circuit<mark style="color:red;">\*</mark> | String  | A recognized debate circuit for the specified format (see API Constants & Schemas for more).                                                   |
| year<mark style="color:red;">\*</mark>    | String  | A recognized debate year for the specified circuit (see API Constants & Schemas for more).                                                     |
| api                                       | Boolean | Whether to provide an API-style response (the currentURL as a String) or human-style response (redirect to the currentURL). Defaults to False. |

{% tabs %}
{% tab title="400: Bad Request Sent if you fail to provide valid information for all required query parameters." %}

```javascript
Invalid Query
```

{% endtab %}

{% tab title="500: Internal Server Error Sent when an unexpected error is encountered. A message is provided that includes what went wrong." %}

```javascript
{
    "message": <(String) error message>
}
```

{% endtab %}

{% tab title="200: OK A redirect is sent if Query.api is False to the currentURL." %}

```javascript
{
    "goTo": <(String) current url>
}
```

{% endtab %}

{% tab title="200: OK The currentURL is sent if Query.api is True." %}

```javascript
{
    "currentURL": <(String) current url>
}
```

{% endtab %}
{% endtabs %}

## Get Leaders

<mark style="color:blue;">`GET`</mark> `URL: https://tournaments.tech/leaders`

Get a leaderboard for a specified format, circuit, and year. Uses the OTR Score to generate leaders.

#### Query Parameters

| Name                                      | Type   | Description                                                                                  |
| ----------------------------------------- | ------ | -------------------------------------------------------------------------------------------- |
| format<mark style="color:red;">\*</mark>  | String | A recognized debate format (see API Constants & Schemas for more).                           |
| circuit<mark style="color:red;">\*</mark> | String | A recognized debate circuit for the specified format (see API Constants & Schemas for more). |
| year<mark style="color:red;">\*</mark>    | String | A recognized debate year for the specified circuit (see API Constants & Schemas for more).   |

{% tabs %}
{% tab title="400: Bad Request Sent if you fail to provide valid information for all required query parameters." %}

```javascript
Invalid Query
```

{% endtab %}

{% tab title="500: Internal Server Error Sent when an unexpected error is encountered. A message is provided that includes what went wrong." %}

```javascript
{
    "message": <(String) error message>
}
```

{% endtab %}

{% tab title="200: OK An array of teams is returned in order of highest to lowest ranked." %}

```javascript
[<(Schemas.Team)>...]
```

{% endtab %}
{% endtabs %}


# API Constants & Schemas

## Constants

Here are the constants referenced in the API documentation and their information.

| Constant | Description                                                                                                                                                                                                     | Value(s)                                                                                                                                               |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| format   | The format of debate to use.                                                                                                                                                                                    | <ol><li><code>PF</code> (Public Forum)</li><li><code>LD</code> (Lincoln Douglas)</li><li><code>CX</code> (Policy)</li></ol>                            |
| circuit  | The circuit of tournaments to use.                                                                                                                                                                              | <p><strong>Circuits for PF:</strong></p><ol><li><code>National</code></li></ol>                                                                        |
| year     | The school year (SY) to use. Formatted as `SY_SS_EE` where `SS` is the last 2 digits of the starting year and `EE` is the last 2 digits of the ending year. `EE` must equal `SS` + 1 (1 school year at a time). | <p><strong>Public Forum:</strong></p><p></p><p><em>National Circuit:</em></p><ol><li><code>SY\_20\_21</code></li><li><code>SY\_21\_22</code></li></ol> |

## Schemas

### Team

```json5
{
    "id": <(String) team id>,
    "codes": [<(String team code>...],
    "otrScore": <(Float) team OTR Score>,
    "goldBids": <(Int) number of gold bids>,
    "silverBids": <(Int) number of silver bids>,
    "prelimRecord": [
        <(Int) overall number of prelim wins>,
        <(Int) overall number of prelim losses>
    ],
    "breakRecord": [
        <(Int) overall number of elim wins>,
        <(Int) overall number of elim losses>
    ],
    "breakPCT": <(Float) decimal percent of tournaments with elim runs>,
    "tournaments": [<(Tournament)>...]
}
```

### Tournament

```json
{
    "tournamentComp": <(Float) OTR tournament comp>,
    "fullNames": <(String) full names as on Tabroom entry>,
    "lastNames": [
        <(String) one of two last names as on Tabroom entry>,
        <(String) one of two last names as on Tabroom entry>
    ],
    "prelimRecord": [
        <(Int) number of prelim wins at this tournament>,
        <(Int) number of prelim losses at this tournament>
    ],
    "breakRecord": [
        <(Int) number of elim wins at this tournament>,
        <(Int) number of elim losses at this tournament>
    ],
    "eliminated": [
        <(Int) number of ballots won in final round>,
        <(Int) number of ballots lost in final round>,
        <(String) final round name as on Tabroom>,
        <(String) final round name standardized>
    ],
    "speaks": [
        {
            "name": <(String) speaker name>,
            "rawAVG": <(String) raw speaker point mean as on Tabroom>,
            "adjAVG": <(String) adjusted [IQR = 2] speaker point mean>,
        },
        {
            "name": <(String) speaker name>,
            "rawAVG": <(String) raw speaker point mean as on Tabroom>,
            "adjAVG": <(String) adjusted [IQR = 2] speaker point mean>,
        },
    ],
    "goldBid": <(Boolean) whether or not a gold bid was earned>,
    "silverBid": <(Boolean) whether or not a silver bid was earned>,
    "breakBoost": <(Int) number of break rounds won + 1>,
    "breakBoost": <(Int) tournament difficulty booost factor>,
    "OPwpm": <(Float) mean opponent number of rounds won>,
    "name": <(String) the name of the tournament as on Tabroom>
}
```


# Examples

Basic Examples to help you familiarize yourself with the API.

## Getting Leaders

{% embed url="<https://codepen.io/therealbaljeet/pen/LYOOZLp>" %}
Getting the leaderboard for the 2020-21 PF National Circuit
{% endembed %}

Here's the Javascript we used.

```javascript
const format = "PF";
const circuit = "National";
const year = "SY_20_21";

const content = document.getElementById("content");

fetch(`https://tournaments.tech/leaders?format=${format}&circuit=${circuit}&year=${year}`).then(response => {
  response.json().then(data => {
    content.innerText = JSON.stringify(data);
  })
});
```

First, we set up our constants - format, circuit, and year. If you need help with these, see [API Constants and Schemas](/api-constants-and-schemas).

Next, we grab a reference to a blank `div` in the DOM so that we can insert our response later.

We use the `fetch` api to ping the `/leaders` endpoint with a `GET` request, and then provide our constants in the query.

After getting the response json, we put it in the `div` we grabbed earlier.

## Getting A Specific Team

{% embed url="<https://codepen.io/therealbaljeet/pen/bGYYeYv>" %}
Getting Bethesda Chevy Chase GT's Information for the 2020-21 PF National Circuit
{% endembed %}

Here's the Javascript we used.

```javascript
const format = "PF";
const circuit = "National";
const year = "SY_20_21";
const team = "6204506bd1a7e6db5199f014";

const content = document.getElementById("content");

fetch(`https://tournaments.tech/query?format=${format}&circuit=${circuit}&year=${year}&team=${team}`).then(response => {
  response.json().then(data => {
    content.innerText = JSON.stringify(data);
  })
});
```

First, we set up our constants - format, circuit, and year. If you need help with these, see [API Constants and Schemas](/api-constants-and-schemas). This time, I added in the team ID for Bethesda Chevy Chase GT, since we're querying for their data specifically.

Next, we grab a reference to a blank `div` in the DOM so that we can insert our response later.

We use the `fetch` api to ping the `/query` endpoint with a `GET` request, and then provide our constants in the query.

After getting the response json, we put it in the `div` we grabbed earlier.

## Searching For A School

{% embed url="<https://codepen.io/therealbaljeet/pen/YzEEWaN>" %}
Querying for all records from "Strake" for the 2020-21 PF National CIrcuit
{% endembed %}

Here's the Javascript we used.

```javascript
const format = "PF";
const circuit = "National";
const year = "SY_20_21";
const term = "Strake";

const content = document.getElementById("content");

fetch(`https://tournaments.tech/query?format=${format}&circuit=${circuit}&year=${year}&term=${term}`).then(response => {
  response.json().then(data => {
    content.innerText = JSON.stringify(data);
  })
});
```

First, we set up our constants - format, circuit, and year. If you need help with these, see [API Constants and Schemas](/api-constants-and-schemas). This time, I added in the term "Strake", since we're querying for their data specifically.

Next, we grab a reference to a blank `div` in the DOM so that we can insert our response later.

We use the `fetch` api to ping the `/query` endpoint with a `GET` request, and then provide our constants in the query.

After getting the response json, we put it in the `div` we grabbed earlier.


# Schemas

In progress, API not available for public use.

## Querying the API

Any models can be accessed via their respective endpoints. To filter them, you can use a request query parameter with the key name and your filter. The filter should be accepted by MongoDB, our back-end database.

### Constants

Here are the constant types used in our API.&#x20;

```typescript
type TabroomAsset = string;
type School = string
type Circuit = string

type ElimRoundName =
    "Finals" |
    "Semifinals" |
    "Quarterfinals" |
    "Octafinals" |
    "Double Octafinals" |
    "Triple Octafinals" |
    "Quadruple Octafinals"

type Bid =
    "full" |
    "partial"

type Event =
    "Public Forum" |
    "Lincoln Douglas" |
    "Policy"

type RoundType =
    "prelim" |
    "elim"

type Side =
    "Pro" |
    "Con" |
    "Aff" |
    "Neg"

type Result =
    "Win" |
    "Loss" |
    "Bye"
```

### Document Fragments

These are the "building blocks" for our main schemas. Unlike the main schemas, they do not have their own collection in our database, but are instead incorporated into parent documents.

```typescript
interface Statistics {
    _id: string;
    prelim_record: [number, number];
    elim_record: [number, number];
    break_pct: number;
    avg_op_wpm: number;
    otr_score: number;
    rank: {
        circuit: {
            season: number;
            all_time: number;
        },
        event: {
            season: number;
            all_time: number;
        }
    },
    speaking_results: {
        [key: ref<Competitor>]: SpeakingResult;
    };
    bids: {
        full: number;
        partial: number;
        ghost_full: number;
        ghost_partial: number;
    }
}

interface SpeakingResult {
    _id: string;
    raw_avg: number;
    adj_avg: number;
}
```

### Models

These 7 models are our main schemas and can be interacted with through their respective endpoints.

If the `ref<T>` generic type is used, it means that the field can be populated to its respective model via the `expand` query parameter. For performance reasons, this should only be done if the data is actually needed.

#### Judge

A judge has at least one `JudgeRecord`. This model should be used for performant judge searching by name. It is available at `/judges`.

```typescript
interface Judge {
    _id: string;
    name: string;

    // TODO: Judge statistics (v3)
}
```

#### Judge Record

A judge record is detailed information about a single round they judged. It is available at `/judge-record`.

```typescript
interface JudgeRecord {
    _id: string;
    decision: Side;
    avg_speaker_points: number;
    round: ref<Round>;
    judge_url: TabroomAsset;
    judge: ref<Judge>;
    type: RoundType;
    event: Event;
    circuit: Circuit;
    tournament: ref<Tournament>;
    timestamp: number;
}
```

#### Entry

An entry is comprised of one or more competitors. They are restricted to one event, but their tournament information can span multiple seasons and circuits. Consequently, we recommend filtering down to specific seasons and/or circuits via the request's query parameters. It is available at `/entries`.

```typescript
interface Entry {
    // Metadata
    _id: string;
    codes: string[];
    competitors: ref<Competitor>[];
    schools: School[];
    statistics?: Statistics; // Computed, Expandable

    // Can be used as filter options
    event: Event;
    circuits: Circuit[];
    seasons: number[];

    // Results
    tournaments: ref<TournamentResult>[];
}
```

#### Tournament Result

A tournament result is an entry's performance data at a given `Tournament`. It is available at `/tournament-results`.

```typescript
interface TournamentResult {
    _id: string;
    tournament: ref<Tournament>;
    code: string;
    competitors: ref<Competitor>[];
    prelim_rank: [number, number];
    prelim_record: [number, number];
    elim_record: [number, number];
    entry_id: number;
    entry_page: TabroomAsset;
    school: School;
    speaking_results: {
        [key: ref<Competitor>]: SpeakingResult;
    };
    bid: Bid;
    is_ghost_bid: boolean;
    op_wpm: number;
    prelim_rounds: ref<Round>[];
    elim_rounds: ref<Round>[];
}
```

#### Round

A round is the result of a specific matchup at a `Tournament`. It is available at `/rounds`.

```typescript
interface Round {
    _id: string;
    name: string;
    name_std: string;
    result: Result;
    decision: [number, number];
    judges: ref<JudgeRecord>[];
    side: Side;
    opponent: ref<Entry>;
    op_wpm: number;
    speaking_results: {
        [key: ref<Competitor>]: SpeakingResult;
    };
}
```

#### Tournament

A tournament is a competition within a specific debate event, circuit, and season. It is available at `/tournamnets`.

```typescript
interface Tournament {
    _id: string;
    name: string;
    start_date: number;
    end_date: number;
    location: string;
    url: TabroomAsset;
    event: Event;
    event_url: TabroomAsset;
    prelim_url: TabroomAsset;
    tourn_id: string;
    circuit: Circuit;
    season: number;
    is_toc_qualifier: boolean;
    bid_level: ElimRoundName;
}
```

#### Competitor

A competitor is an individual. They have at least 1 associated `Entry`, but can have multiple (including across various events). This model should be used for performant debater searching by name, or to find out all activity of a specific debater. It is available at `/competitors`.

```typescript
interface Competitor {
    _id: string;
    name: string;
}
```


