# Pyot Documentation

{% hint style="danger" %}

#### Caution

This package is now DEPRECATED and will no longer receive new updates. It has proved to be overly complex, hard to customize or extend, and contains an increased amount of implicit syntaxes that go against best practices. New projects shall NOT use this package and old projects should move away from this package as soon as possible.

The recommended package is [pulsefire](https://github.com/iann838/pulsefire). A modern and flexible Riot Games Python SDK. Built to be simple to use, highly configurable, and extendable. Objects and client responses are fully typed to speed up coding efficiency.
{% endhint %}

## Pyot

Pyot is an asyncIO-based high-level Python Riot Games API framework that encourages rapid development and clean, pragmatic design. It takes care of much of the hassle of the Riot Games API, so developers can focus on writing apps without needing to reinvent the wheel. It’s free and open source.

| Index  | Version |
| ------ | ------- |
| PyPI   | `6.0.9` |
| master | `6.0.9` |

If you're migrating your project to a newer version of Pyot, please refer to **Changelog** section of the documentation.

### Features

Features that Pyot can provide for your projects.

* ***AsyncIO Based***: Performing 60x faster with AsyncIO, highly configurable settings, and a wide range of tools to speed I/O tasks.
* ***Community Projects Integrations***: Includes support for CDragon, MerakiCDN. DDragon for LoL is Forbidden due to incompatible APIs.
* ***Caches Integrated***: A wide range of Caches Stores is available out of the box, and currently supports Omnistone(Runtime), RedisCache(RAM), DiskCache(Disk), and MongoDB(NoSQL).
* ***Multiple Models***: Available models of League of Legends, Teamfight Tactics, Legends of Runeterra and VALORANT.
* ***Code Autocompletion***: Access data through attributes and properties, and maximize code efficiency with code autocompletion.
* ***Perfect Rate Limiter***: Pyot's Rate Limiter is production-tested in all asynchronous, multithreaded, and multiprocessed environments.
* ***User-Friendly Docs***: Human readable documentation that covers guides and all the available high-level and low-level APIs in Pyot.

If this framework is useful, consider giving a **star** to the repo.

### Documentation

The documentation covers:

* Installation.
* Configuration.
* Base Objects.
* Concurrency Basics.
* Examples.
* Models API.
* Stores.
* Limiters.
* Utilities.
* Integrations.
* Changelog.

Due to the complexity of the framework, there is no quick-start guide, it is recommended to start with:

* Reading and understanding the **Cores** section of the documentation.
* Reading and understanding the example projects at **Examples** section to get familiar.
* If your project requires a specific integration, check out **Integrations** section.


# Cores


# Installation

Pyot requires Python **3.7** or newer.

Get the latest version of Python at <https://www.python.org/downloads/> or with your operating system’s package manager.

## Pip

Installing an official release with pip:

```shell
pip install pyot -U
```

## Github

Installing from source code master, it may include hotfixes and unstable code:

```shell
pip install git+https://github.com/iann838/Pyot.git
```

## Extras

Depending on the need, installation of extras may be needed:

```shell
pip install pyot[diskcache]     # installs: ["diskcache>=5.1", "asgiref>=3.2"]
pip install pyot[redis]         # installs: ["redis[hiredis]>=4.5.0"]
pip install pyot[mongodb]       # installs: ["motor>=2.3"]
pip install pyot[test]          # installs: ["typeguard>=2.13"] + all above
```


# Configuration

Pyot requires many setups and configurations in order to work properly. Models cannot be imported until the related confs are evaluated (raises `Model ... is inactive`).

These configurations generally stays all packed in a single file (generally called `conf.py` or `pyotconf.py`) or one file per model, they must be loaded **once and only once** at application startup.

{% hint style="info" %}
If an integration of Pyot to another framework is needed. General integration guides are provided for Django, FastAPI and Celery projects at the **Integrations** section. These guides does not require to be strictly followed and only serves for reference purposes.
{% endhint %}

## Models

Module: `pyot.conf.model`

Configurable models: `riot`, `lol`, `tft`, `lor`, `val`

Setup, configure and activate the models of need. Define the default platform, region, version and locale, these are used for default values in class init definitions and object bridging. For a list of correct platforms and regions, they are documented under each models main page.

### *function* `activate_model` -> `decorator(ModelConf)`

* Arguments:
  * `name`: Name of model

### *class* `ModelConf`:

* Attributes:
  * `default_platform`: `str`
  * `default_region`: `str`
  * `default_version`: `str`
  * `default_locale`: `str`

## Pipelines

Module: `pyot.conf.pipeline`

Configurable models: `lol`, `tft`, `lor`, `val`

Setup, configure and activate a pipeline for the activated models. A pipeline is a list of prioritized data sources that a model will request data from. You may define and activate multiple pipelines under some circumstances, but one and only one default pipeline must exists for each model.

{% hint style="danger" %}
Global models are not configurable as an standalone pipeline, such as `riot`, because objects of multiple other models can access these models objects in their own pipelines. They are configured as part of other models pipelines.
{% endhint %}

### *function* `activate_pipeline` -> `decorator(PipelineConf)`

* Arguments:
  * `name`: Name of model this pipeline belongs to

### *class* `PipelineConf`:

* Attributes:
  * `name`: `str`

    > Name of this pipeline
  * `default`: `bool`

    > Is this the default pipeline
  * `stores`: `List[Dict[str, Any]]`

    > List of data stores, the earlier the store is located in the list, the higher the priority. Each store has it's own configurations, they are documented in the Stores section.

{% hint style="info" %}
A variety of stores is provided, including caches (e.g. Redis, MongoDB) and services (e.g. RiotAPI, CDragon). Cache stores must be placed at higher priority than any service stores.

For cache stores, multiple stores of the same backend can be configured, it may be useful for scenarios where different types of objects should be cached in different places (e.g. Two `DiskCache`s with different directory for storing lol matches and lol timelines).
{% endhint %}

## Imports

{% hint style="info" %}
If you wish to pack everything into a single file project, put all conf at the top of your file, and skip importing. This is generally not recommended.
{% endhint %}

Conf files can be imported in following ways:

* Using `import_confs` from `pyot.conf.utils`.
* Manually importing the conf file in python syntax.
* If an integration is being used (e.g. Django), check if the integration has a custom conf import hook and use it instead.

### *function* `import_confs` -> `None`

* Arguments:
  * `path_or_paths`: Import path or list of import paths to the conf files.

{% hint style="info" %}
Import path is the path used as if the file/module is being imported using python syntax via `import`, `__import__` or `importlib.import_module`.
{% endhint %}

## Example

Example configuration of `lol` model and a default pipeline including a cache store that caches summoners for 100 seconds, matches and timelines for 10 minutes; and service stores for CDragon and RiotAPI:

```python
# myproject/pyotconf.py

from pyot.conf.model import activate_model, ModelConf
from pyot.conf.pipeline import activate_pipeline, PipelineConf


@activate_model("lol")
class LolModel(ModelConf):
    default_platform = "na1"
    default_region = "americas"
    default_version = "latest"
    default_locale = "en_us"


@activate_pipeline("lol")
class LolPipeline(PipelineConf):
    name = "lol_main"
    default = True
    stores = [
        {
            "backend": "pyot.stores.omnistone.Omnistone",
            "expirations": {
                "summoner_v4_by_name": 100,
                "match_v4_match": 600,
                "match_v4_timeline": 600,
            }
        },
        {
            "backend": "pyot.stores.cdragon.CDragon",
        },
        {
            "backend": "pyot.stores.riotapi.RiotAPI",
            "api_key": os.environ["RIOT_API_KEY"],
        }
    ]
```

Now import the confs into your application:

```python
# myproject/myapp.py

from pyot.conf.utils import import_confs

import_confs("pyotconf")
```


# Objects

Pyot provides many models, each model contains Pyot classes, and these classes creates instances of Pyot objects. Different types of Pyot objects has different structure and functionalities.

Module: `pyot.core.objects`

{% hint style="info" %}
This page documents the bases of Pyot classes and objects, for reference of models, please go to **Models** section.
{% endhint %}

## Pyot Static

Base class: `PyotStaticBase`

Takes a Python data and serialize it into Python objects based on defined type hints of its subclass. Attributes may return other Pyot Static objects, these objects are initially not serialized, instead are assigned as instances of `PyotLazy`, these objects will only be serialized upon first access of the attribute.

{% hint style="info" %}
Some internal info has been hidden, to learn more please review source code instead.
{% endhint %}

### *class* `PyotStaticBase`

Metaclass: `PyotMetaClass`

Extends: `PyotRoutingBase`

Definitions:

* `__getitem__` -> `Any`

  > Supports `object[item]` syntax, by using this syntax instead of `object.item` will return the `.dict()` representation of `object.item`, useful for avoiding serialization of `object.item` beforehand if unwanted.

Attributes:

* `_meta`: `Self.Meta`

Properties:

* `region` -> `str`
* `platform` -> `str`
* `version` -> `str`
* `locale` -> `str`
* `metaroot` -> `PyotCoreBase`

  > Returns the root Pyot Core object which this object is child of.
* `metapipeline` -> `Pipeline`

  > Returns the pipeline of this object's class' model.

Methods:

* *method* `dict` -> `Dict`

  * `force_copy`: `bool = False`

    > Force make deep copy before returning, default to `False` for shallow copy.

  * `lazy_props`: `bool = False`

    > True for loading all `lazy_property`s before returning, False otherwise.

  * `recursive`: `bool = False`

    > True for returning `rdict()` content instead.

  > Returns the Python dict that is used for serialization on this object.
* *method* `rdict` -> `Dict`

  > Returns the Python representation of object by doing recursive walks on itself.

## Pyot Core

Base class: `PyotCoreBase`

Inherits all functionalities of `PyotStaticBase`. This type of objects has the ability to request for data on pipelines.

{% hint style="info" %}
Some internal info has been hidden, to learn more please review source code instead.
{% endhint %}

### *class* `PyotCoreBase`

Extends: `PyotStaticBase`

Methods:

* `using` -> `None`

  * `pipeline_name`: `str`

  > Set the pipeline used for request in this instance.
* `query` -> `Self`

  > Add query parameters to the request. This method is only present if the class accepts query parameters.
* `body` -> `Self`

  > Add body parameters to the request. This method is only present if the class requires body parameters.
* *async* `token` -> `PipelineToken`

  > Create a pipeline token that identifies this object.
* *async* `get` -> `Self`

  * `force_copy`: `bool = False`

  > Make a GET request to the pipeline.
  >
  > * `force_copy`: Force make a deep copy on raw before serializing, default to `False` for smart copy (multi-level shallow copy).
* *async* `post` -> `Self`

  * `force_copy`: `bool = False`

  > Make a POST request to the pipeline.
  >
  > * `force_copy`: Force make a deep copy on raw before serializing, default to `False` for smart copy (multi-level shallow copy).
* *async* `put` -> `Self`

  * `force_copy`: `bool = False`

  > Make a PUT request to the pipeline.
  >
  > * `force_copy`: Force make a deep copy on raw before serializing, default to `False` for smart copy (multi-level shallow copy).
* `raw` -> `Any`

  > Returns the raw response of the request, by default smart copy is used, therefore there could be differences and should not be modified, a safer option is use `force_copy` flag to do deepcopy of the response at the cost of performance.
* *classmethod* `load` -> `Self`

  * `raw`: `Any`

  > Return an instance of the class and load the submitted raw data.

## Pyot Utils

Base class: `PyotUtilBase`

This type of objects are meant to be utilities objects. The definition of this base class is empty, it is only used for generating documentations and possible usecases involving `isinstance`.

## Example

Get ranked solo/duo match ids of the last 24 hours for a summoner by name and platform. Assuming model is activated and pipeline properly configured.

```python
from datetime import datetime, timedelta

from pyot.models import lol
from pyot.utils.lol.routing import platform_to_region


async def get_match_ids(name: str, platform: str) -> List[str]:
    summoner = await lol.Summoner(name=name, platform=platform).get()
    match_history = await lol.MatchHistory(
        puuid=summoner.puuid,
        region=platform_to_region(summoner.platform)
    ).query(
        count=100,
        queue=420,
        start_time=datetime.now() - timedelta(days=200)
    ).get()
    return match_history.ids
```


# Concurrency

One of the benefits of asynchronous programming is that it allows concurrency. In Python, multiple tasks can start, run and complete in overlapping time periods.

Pyot provides a production rated queue manager to achieve high magnitude of concurrency in a stable manner.

## Queue

Module: `pyot.core.queue`

Worker queue for scheduling coroutines concurrently.

{% hint style="info" %}
This object is only accessible as a context manager with the `async with` syntax for safeguarding session closing and workers joining.
{% endhint %}

### *class* `Queue`

Definitions:

* `__init__` -> `None`
  * `workers`: `int = 25`

    > Maximum number of workers to spawn for the queue. Increasing the number of workers may increase or decrease performance. Defaults to 25.
  * `maxsize`: `int = None`

    > Max size of the queue. Defaults to `workers * 2`.
  * `log_level`: `int = 0`

    > Log level for the que (does not affect pipeline logs). Defaults to 0 (NOLOG level).
  * `exception_handler`: `Callable[[Exception], Any] = LOGGER.warning`

    > Handler for raised exceptions in workers, defaults to logging a message with level 30 (WARNING).

Attributes:

* `queue`: `asyncio.Queue`
* `workers_num`: `int`
* `maxsize`: `int`
* `responses`: `Dict`
* `counter`: `int`
* `workers`: `List`
* `exception_handler`: `Callable[[Exception], Any]`

Methods:

* *async* `put` -> `None`

  * `coro`: `Coroutine`

    > Coroutine to put on the queue.

  * `delay`: `float`

    > Amount of delay in seconds before putting the coroutine into the queue. Defaults to 0 (No delay).

  > Put a coroutine object to the queue. If the queue is full, wait for availability. A delay may be provided if desired for load balancing.
* *async* `join` -> `List[T]`

  * `class_of_t`: `Optional[Type[T]]`

    > Optional, Generic type for typing the return content of this method (e.g `await queue.join(int)` will return a list typed as `List[int]`).

  > Block until all items in the queue have been received and executed. Clears the previously collected items if exists. NoneType and Exceptions are not collected, order of items is maintained but not guaranteed.

{% hint style="info" %}
You can use the same queue to `join()` as many time as you want, it will clear previous collected responses, this creates a nice way to do everything in a single Queue. Method `join()` will be automatically called before exiting the `async with` block, it is not needed to call explicitly unless the content of it is needed.
{% endhint %}

## Example

```python
from pyot.models import lol
from pyot.core.queue import Queue

async def get_puuid(summoner: lol.Summoner):
    summoner = await summoner.get()
    return summoner.puuid

async def pull_puuids():
    async with Queue() as queue:
        await queue.put(lol.ChallengerLeague(queue="RANKED_SOLO_5x5", platform="na1").get())
        await queue.put(lol.MasterLeague(queue="RANKED_SOLO_5x5", platform="na1").get())
        leagues = await queue.join(lol.ChallengerLeague) # Param is optional, used for typing only

        summoners = []
        for league in leagues:
            for entry in league.entries:
                summoners.append(entry.summoner)

        for summoner in summoners:
            await queue.put(get_puuid(summoner))
        return await queue.join(str)
```

{% hint style="danger" %}
Try not to return anything or return small objects in coroutines passed to the queue, because `Queue` will save those return values for the `join()`, meaning that memory can start to increase over time. Design async functions to consume data instead.

Objects instantiated outside of async functions holds reference in an upper scope, these objects can become a source of memory leaks if the data it holds (or will hold after mutations executed by coroutines) is big enough. To counter this:

* Instantiate objects inside async functions (e.g. Pass the id and region of a match and instantiate inside the function instead of passing a `lol.Match` object), so it can be garbage collected when it goes out of scope and nothing else holds reference to it.
* If the objects are inside an iterable and is planned to be mutated and filled with more data (e.g. calling `.get()` on PyotCore objects), freeze the iterable with an utility container `pyot.utils.itertools.FrozenGenerator`, it creates exact copies of the objects when iterated, therefore dropping the outer scope reference, the original object will be left intact.
  {% endhint %}

Assuming the need to collect 30k matches, this will lead to a high use of memory:

```python
# ... imports

async def get_matches():
    matches = list_with_30k_matches
    async with Queue() as queue:
        for match in matches:
            await queue.put(match.get())
```

Instead, consume the matches directly instead of collecting them. The list is frozen to prevent memory usage since the child function calls `.get()` on the match.

```python
# ... imports
from pyot.utils.itertools import frozen_generator

async def get_matches():
    matches = list_with_30k_matches
    matches = frozen_generator(matches) # Freezes the list to prevent mutation
    async with Queue() as queue:
        for match in matches:
            await queue.put(consume_match(match))

async def consume_match(match):
    match.get() # pass the session to reuse
    # ...
    # Consume your match (e.g. get specific stat, mutate a dictionary, save to db, etc.) ...
    # ...
    return None
    # OR no return (When no return is stated, returns None by default)
```


# Resources

{% hint style="info" %}
This page is of importance if your project is multithreaded, if you are not sure if your project is multithreaded, Pyot will automatically detect such environment and sends a warning. **You may choose to ignore this page if**:

* Your project runs single threaded or;
* Your project runs smoothly without implementing these concepts or;
* You choose to ignore all resource warnings and errors since they do not affect the main functionality of your prorject.
  {% endhint %}

Pyot acquires resources internally on-demand, on a traditional single threaded program, these resources remains mostly constant throughout the its runtime, and released automatically after the program exits.

However not all projects can be single threaded (usually unavoidable and required by other frameworks such as Django, Flask, DramatiQ, Celery, etc.), on a multithreaded environment, there is an event loop for each running thread, and these threads will be rotating and so the event loops, this causes two main issues for Pyot:

* Pyot must acquire new resources every time a new event loop attempts to use them, because most asyncio libraries and frameworks that Pyot is depending on are not designed to work in multithreaded environments (objects are bound to a specific event loop, limitations by python asyncio `Future`s and `Task`s).
* Pyot have no way to know when to release the unused resources because if they are released before the workflow is done, it will cause issue to the workflow, if they are released after the event loop is closed, the releasing functions cannot run because most of them requires to run on the same event loop before closing, and there is no 'magical' way of knowing the exact time of "right before event loop close".

For these reasons if you decide to use Pyot in a multithreaded environment, you will be responsible in telling Pyot "when" should it acquire and release resources in an event loop. Similar to other libraries using `async with` for management.

Alternatively, you can choose to simply ignore all warnings and errors, as they *likely won't* prevent the code from running, Pyot will automatically check if there are resources in closed event loops and forcefully kill them to prevent memory leaks, this is of course not good pratice nor a graceful way of handling resources.

## Interfaces

Module: `pyot.core.resources`

### *class* `ResourceManager`

Ensures acquisition and releasing of resources used by Pyot. Used as async context manager or as async function decorator.

Definitions:

* `__init__` -> `None`
  * `exist_ok`: `bool = False`

    > If another resource manager is currently active in the event loop, skip this context, avoid using this flag unless unavoidable.
* `__aenter__` -> `Self`

  > Acquire resources bound to instantiated event loop.
* `__aexit__` -> `None`

  > Release resources bound to instantiated event loop.

Properties:

* `loop` -> `asyncio.AbstractEventLoop`
* `exist_ok` -> `bool`

Methods:

* *classmethod* `as_decorator` -> `F~AsyncCallable`

  * `func`: `F~AsyncCallable`

  > Return a decorator that can be used for decorating async functions instead of using as context manager.
* *asyncmethod* `acquire` -> `Self`

  > Explicit equivalent of `__aenter__`.
* *asyncmethod* `release` -> `None`

  > Explicit equivalent of `__aexit__`.

### *alias* `resource_manager` \~ `ResourceManager`

### *global* `resource_managed_loops`: `Set[asyncio.AbstractEventLoop]`

Set of event loops with active resource managers.

### *class* `ResourceTemplate`

Template for acquiring resources bound to event loops. The submitted functions **must not implement locks**, it may cause deadlocks because the acquisition and releasing process are also behind a lock.

Extends:

* `Generic[R]`

Definitions:

* `__init__` -> `None`
  * `acquire_func`: `Callable[[], Union[R, Awaitable[R]]]`

    > Function for acquiring the resource, the return value will be awaited if it is a coroutine.
  * `release_func`: `Callable[[R], Any] = ...`

    > Function for releasing the resource, the return value will be awaited if it is a coroutine. Optional.

Properties:

* `acquire_func`: `Callable[[], Union[R, Awaitable[R]]]`
* `release_func`: `Callable[[R], Any] = ...`

Methods:

* *asyncmethod* `acquire` -> `Self`

  * `loop`: `asyncio.AbstractEventLoop = ...`

  > Acquire resource using `acquire_func` bound to the event loop, default current event loop if not provided.
* *asyncmethod* `release` -> `None`

  * `loop`: `asyncio.AbstractEventLoop = ...`

  > Release resource using `release_func` bound to the event loop, default current event loop if not provided.
* *asyncmethod* `purge` -> `None`

  > Purge acquired resources for all closed loops. Ungraceful release.

### *global* `resource_templates`: `List["ResourceTemplate"]`

List of all instantiated resource templates by Pyot.

## Example

This example only serves for reference purpose only, there is zero reason to involve threads when the framework itself is async. Here theoretically `average_match_duration_millis` will run in threads on its own event loop, a `resource_manager` is used to properly acquire and release the resources (think of it as another `async with aiohttp.ClientSession()` but a much more complex one).

```python
from concurrent.futures import ThreadPoolExecutor
from typing import List
import statistics

from pyot.core.resources import resource_manager
from pyot.core.queue import Queue
from pyot.models import lol
from pyot.utils.sync import async_to_sync


@async_to_sync
async def average_match_duration_millis(summoner_name: str):
    # This function runs in a different thread and event loop
    async with resource_manager(), Queue() as queue:
        # At this point, resources are acquired for this event loop
        summoner = await lol.Summoner(name=summoner_name).get()
        history = await summoner.match_history.get()
        for match in history.matches[:5]:
            await queue.put(match.get())
        first_5_matches: List[lol.Match] = await queue.join()
    # At this point, resources are released for this event loop
    return statistics.mean([match.info.duration_millis for match in first_5_matches] or [0])

summoner_names = [...]
futures = []
with ThreadPoolExecutor() as executor:
    for summoner_name in summoner_names:
        futures.append(executor.submit(average_match_duration_millis, summoner_name))
    for future in futures:
        future.result()
```

There is a decorator version of resource manager, by decorating an async function instead of using as context manager, it will acquire resources before the function gets called and release them after the function is called. This may be more elegant for decorating functions like Django Views, DramatiQ or Celery tasks, etc.

```python
@async_to_sync
@resource_manager.as_decorator
async def average_match_duration_millis(summoner_name: str):
    # Before entering scope, resources are acquired for this event loop
    async Queue() as queue:
        summoner = await lol.Summoner(name=summoner_name).get()
        history = await summoner.match_history.get()
        for match in history.matches[:5]:
            await queue.put(match.get())
        first_5_matches: List[lol.Match] = await queue.join()
    return statistics.mean([match.info.duration_millis for match in first_5_matches] or [0])
    # After exiting scope, resources are released for this event loop
```


# Exceptions

List of exceptions that Pyot uses.

Module: `pyot.core.exceptions`

### *class* `PyotException`

Extends: `Exception`

Base Pyot exception class.

### *class* `NotFindable`

Extends: `PyotException`

Message: \[600] Pipeline token matching pair not found

### *class* `SessionNotFound`

Extends: `PyotException`

Message: \[601] Session Not Found.

### *class* `DecodeError`

Extends: `PyotException`

Message: \[602] AioHttp took too long to decode the response.

### *class* `NoContent`

Extends: `PyotException`

Message: \[204] No Content.

### *class* `NotFound`

Extends: `PyotException`

Message: \[404] Data Not Found.

### *class* `MethodNotAllowed`

Extends: `PyotException`

Message: \[405] Method Not Allowed.

### *class* `ServerError`

Extends: `PyotException`

Messages: `Mapping[int, str]`

* 500: Internal Server Error
* 502: Bad Gateway
* 503: Service Unavailable
* 504: Gateway Timeout \[{code}] {self.messages\[code]}.

### *class* `RateLimited`

Extends: `PyotException`

Message: \[429] Rate limit Exceeded.

### *class* `Forbidden`

Extends: `PyotException`

Message: \[403] Access Forbidden.

### *class* `Unauthorized`

Extends: `PyotException`

Message: \[401] Access Unauthorized.

### *class* `BadRequest`

Extends: `PyotException`

Message: \[400] Bad Request.

### *class* `Timeout`

Extends: `PyotException`

Message: \[408] Timeout Connection.

### *class* `UnidentifiedResponse`

Extends: `PyotException`

Message: \[{code}] Unidentified Response {code}.


# Warnings

List of warnings that Pyot uses.

### *class* `PyotConfWarning`

Extends: `UserWarning`

### *class* `PyotPipelineWarning`

Extends: `RuntimeWarning`

### *class* `PyotStoreWarning`

Extends: `RuntimeWarning`

### *class* `PyotRuntimeWarning`

Extends: `RuntimeWarning`

### *class* `PyotResourceWarning`

Extends: `ResourceWarning`


# Examples


# Single File

Characteristics:

* Structure: Single File.
* Threading: Single Threaded.

Pros:

* Playground style, easy for testing small features.

Cons:

* Conf and working code mixed together, reducing code readability.
* Some linters may complain about `wrong-import-order`, due to conf must be placed before `from pyot.models import ...`. Can be disabled in linter conf files (e.g. `.pylintrc`)

## Structure

File: `main.py`

```python
import asyncio
import sys
import os
from typing import List

from pyot.core.queue import Queue

from pyot.conf.model import activate_model, ModelConf
from pyot.conf.pipeline import activate_pipeline, PipelineConf


@activate_model("lol")
class LolModel(ModelConf):
    default_platform = "na1"
    default_region = "americas"
    default_version = "latest"
    default_locale = "en_us"


@activate_pipeline("lol")
class LolPipeline(PipelineConf):
    name = "lol_main"
    default = True
    stores = [
        {
            "backend": "pyot.stores.omnistone.Omnistone",
            "expirations": {
                "summoner_v4_by_name": 100,
                "match_v4_match": 600,
                "match_v4_timeline": 600,
            }
        },
        {
            "backend": "pyot.stores.cdragon.CDragon",
        },
        {
            "backend": "pyot.stores.riotapi.RiotAPI",
            "api_key": os.environ["RIOT_API_KEY"],
        }
    ]


from pyot.models import lol


async def last_played_champs(summoner_name: str):
    async with Queue() as queue:
        summoner = await lol.Summoner(name=summoner_name).get()
        history = await summoner.match_history.get()
        for match in history.matches[:10]:
            await queue.put(match.get())
        first_10_matches: List[lol.Match] = await queue.join()
    champ_names = []
    for match in first_10_matches:
        for participant in match.info.participants:
            if participant.puuid == summoner.puuid:
                champ_names.append(participant.champion_name)
    return champ_names


if __name__ == "__main__":
    print("Summoner name:", sys.argv[1])
    last_played_champ_names = asyncio.run(last_played_champs(sys.argv[1]))
    print(
        "Last played champ names (last 10 matches):",
        last_played_champ_names,
    )
```

## Run

```
python main.py <summoner_name>
```


# Multi Root

Characteristics:

* Structure: Multi Root.
* Threading: Single Threaded.

Pros:

* Playground style, easy for testing small features.
* Conf and working code are in separate files.

Cons:

* Some linters may complain about `wrong-import-order`, due to conf must be placed before `from pyot.models import ...`. Can be disabled in linter conf files (e.g. `.pylintrc`) or use python syntax `import ...` instead of `import_confs(...)` but that will cause linters to complain `unused-import`.

## Structure

File: `main.py`

```python
import asyncio
import sys
from typing import List
import statistics

from pyot.conf.utils import import_confs
import_confs("pyotconf")
from pyot.core.queue import Queue
from pyot.models import lol


async def average_match_duration_millis(summoner_name: str):
    async with Queue() as queue:
        summoner = await lol.Summoner(name=summoner_name).get()
        history = await summoner.match_history.get()
        for match in history.matches[:10]:
            await queue.put(match.get())
        first_10_matches: List[lol.Match] = await queue.join()
    return statistics.mean([match.info.duration_millis for match in first_10_matches] or [0])


if __name__ == "__main__":
    print("Summoner name:", sys.argv[1])
    avr_match_duration_millis = asyncio.run(average_match_duration_millis(sys.argv[1]))
    print(
        "Average match duration (last 10 matches):",
        avr_match_duration_millis,
        "milliseconds", "(~",
        avr_match_duration_millis / 1000 / 60, "minutes)"
    )
```

File: `pyotconf.py`

```python
import os
from pyot.conf.model import activate_model, ModelConf
from pyot.conf.pipeline import activate_pipeline, PipelineConf


@activate_model("lol")
class LolModel(ModelConf):
    default_platform = "na1"
    default_region = "americas"
    default_version = "latest"
    default_locale = "en_us"


@activate_pipeline("lol")
class LolPipeline(PipelineConf):
    name = "lol_main"
    default = True
    stores = [
        {
            "backend": "pyot.stores.omnistone.Omnistone",
            "expirations": {
                "summoner_v4_by_name": 100,
                "match_v4_match": 600,
                "match_v4_timeline": 600,
            }
        },
        {
            "backend": "pyot.stores.cdragon.CDragon",
        },
        {
            "backend": "pyot.stores.riotapi.RiotAPI",
            "api_key": os.environ["RIOT_API_KEY"],
        }
    ]
```

## Run

```
python main.py <summoner_name>
```


# Module Based

Characteristics:

* Structure: Module based.
* Threading: Single Threaded.

Pros:

* Better code structure for higher maintainability.
* Conf imports is handled by `__init__.py` only for cleaner imports.
* Although not used in this example, using `__main__.py` may be more elegant in some situations.

Cons:

* No significant cons.

## Structure

File: `module/__init__.py`

```python
from pyot.conf.utils import import_confs

import_confs("module.pyotconf")
```

File: `module/pyotconf.py`

```python
import os
from pyot.conf.model import activate_model, ModelConf
from pyot.conf.pipeline import activate_pipeline, PipelineConf


@activate_model("lol")
class LolModel(ModelConf):
    default_platform = "na1"
    default_region = "americas"
    default_version = "latest"
    default_locale = "en_us"


@activate_pipeline("lol")
class LolPipeline(PipelineConf):
    name = "lol_main"
    default = True
    stores = [
        {
            "backend": "pyot.stores.omnistone.Omnistone",
            "expirations": {
                "summoner_v4_by_name": 100,
                "match_v4_match": 600,
                "match_v4_timeline": 600,
            }
        },
        {
            "backend": "pyot.stores.cdragon.CDragon",
        },
        {
            "backend": "pyot.stores.riotapi.RiotAPI",
            "api_key": os.environ["RIOT_API_KEY"],
        }
    ]
```

File: `module/tasks.py`

```python
from typing import List
import statistics

from pyot.core.queue import Queue
from pyot.models import lol


async def average_win_rate_10_matches(summoner_name: str):
    async with Queue() as queue:
        summoner = await lol.Summoner(name=summoner_name).get()
        history = await summoner.match_history.get()
        for match in history.matches[:10]:
            await queue.put(match.get())
        first_10_matches: List[lol.Match] = await queue.join()
    wins = []
    for match in first_10_matches:
        for participant in match.info.participants:
            if participant.puuid == summoner.puuid:
                wins.append(int(participant.win))
    return statistics.mean(wins or [0])
```

File: `main.py`

```python
import asyncio
import sys

from module.tasks import average_win_rate_10_matches


if __name__ == "__main__":
    print("Summoner name:", sys.argv[1])
    average_win_rate = asyncio.run(average_win_rate_10_matches(sys.argv[1]))
    print(
        "Average win rate (last 10 matches):",
        average_win_rate * 100, "%"
    )
```

## Run

```
python main.py <summoner_name>
```


# Pipeline

A data pipeline is a series of caches, databases, and data sources that provides and/or stores data, they are generally called as "stores". The data pipeline is a list of data stores, where the order the data stores specifies how data is pulled and stored. Usually faster data stores go at the beginning of the data pipeline.

When data is queried, a query token is constructed containing the information needed to uniquely identify an object in a data source (e.g. a `region` and `summoner.id` are required when querying for `Summoner` objects). This query is passed up the data pipeline through, and at each data store in the data pipeline asks if that source can supply the requested object. If the store can supply the object (for example, if the object is in the cache, or if the Riot API can send the object/data), it is returned. If the source does not supply the object, the next data store in the pipeline is queried. If no data store can provide an object for the query, a `pyot.core.exceptions.NotFindable` is thrown.

After an object is returned by a data store, the object gets passed backwards in the pipeline. Any data store placed before the store that returned the object will attempt to store the data (e.g. cache it).

Each model requires to have its own pipelines configured.


# Expirations

Expirations are used across all cache stores for managing expiration of cached objects. The configuration accepts a dictionary of endpoint key mapping to a timedelta object or number of seconds.

Global models (e.g. `riot`) will be available to all pipelines, meaning that it is allowed modify endpoints of those models in any pipeline, for example the `"account_v1_by_puuid"` in a pipeline bound to the `val` model and such object called using the `val` pipeline will use such expirations.

{% hint style="info" %}
Only data returned by the `get()` method is sinked through the pipeline, and thus the only ones that can be cached and use expirations.
{% endhint %}

By configuring this argument, it overrides the default endpoints expirations.

## Default Expiration

All expirations defaults to `0` (No-Cache). Except for static data sources (e.g. cdragon, meraki, ddragon, etc.) which defaults to `timedelta(minutes=20)` (20 minutes) due to its frequency of data changes and helping to decrease traffic load.

## Example

This will override the `summoner_v4_by_name` endpoint to cache 2 minutes, and `league_v4_challenger_league` to cache for 10 minutes, and leaving the rest of the default expirations untouched.

```python
    # ... Other Stores
        # ... Other Store Configs
        "expirations": {
            "summoner_v4_by_name": 120,
            "league_v4_challenger_league": 600, # or timedelta(minutes=10)
        }
```


# Handler

Error handlers are used across all service stores for error handling, backoffs and managing request exceptions. The configuration accepts a dictionary of status code mapping to a tuple of strategy arguments.

* Syntax: `Mapping[int, Tuple[str, List[int]]]`

The key is an integer indentifying the status code, the value is a Tuple that has 2 items, the first item is the strategy token and the second item is a list of arguments passed to the strategy. List of tokens and accepted arguments:

* `"T"` (throw on error): `[]` No arguments
* `"R"` (retry a set time): `[times: int]` The number of times to retry
* `"E"` (exponential backoff): `[initial: int, times: int]` The number of seconds for initial backoff and the max number of times to backoff. Each backoff will raise the backoff time to the power of 2.

The functionality of this argument is to define the strategy to use when a non 200 status code is returned for the request made out to other sources. By passing this will override the default strategy specified in the dictionary

{% hint style="info" %}
The Riot Games API can give 3 types of 429: `service`, `application` and `method`, the one that can be overridden is only the `service` 429, the other 2 types of 429 is handled by the rate limiters.
{% endhint %}

## Default Handler

{% hint style="info" %}
Code 800 is for unidentified status codes that are not in the list, for example: a 510 will result in a 800 containing the error code.
{% endhint %}

* `204 : ("T", [])`
* `400 : ("T", [])`
* `401 : ("T", [])`
* `403 : ("T", [])`
* `404 : ("T", [])`
* `405 : ("T", [])`
* `408 : ("E", [3, 3])`
* `429 : ("E", [3, 3])`
* `500 : ("E", [3, 3])`
* `502 : ("E", [3, 3])`
* `503 : ("E", [3, 3])`
* `504 : ("E", [3, 3])`
* `602 : ("R", [2])`
* `800 : ("T", [])`

## Example

This will override the strategy used for 404 to throw inmediately, 502 to retry 3 times before throwing and 429 to exponentially backoff with a initial backoff of 3 seconds and a max tries of 3 times.

```python
    # ... Other Stores
        # ... Stores configurations
        "error_handler": {
            404: ("T", []),
            502: ("R", [3]),
            429: ("E", [3, 3]),
        }
        # ...
```


# Object

Each created pipeline can be accessed in a dictionary at the `pyot.conf.pipeline` module. The key of the pipeline is the name provided in the configuration, if the pipeline is set as default, model name can be used aswell.

```python
from pyot.conf.pipeline import pipelines

lol_pipeline = pipelines["lol"]
```

### *class* `Pipeline`

Definitions:

* `__init__`
  * `model`: `str`

    > Model of belonging.
  * `name`: `str`

    > Name of pipeline.
  * `stores`: `List[Store]`

    > List of Stores to add on the pipeline.
* `__iter__`

  > Iterates over `stores`.
* `__getitem__`

  > Get store by index.

Attributes:

* `model`: `str`
* `name`: `str`
* `stores`: `List[StoreObject]`
* `sessions`: `ResourceTemplate[aiohttp.ClientSession]`

Methods:

* *async* `get` -> `Any`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.
* *async* `set` -> `None`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.
  * `value`: `Any`

    > Data to be stored in qualified stores.
  * `stop`: `Store = None`

    > The instance of the store that it should stop at (not sink further).
* *async* `post` -> `Any`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.
  * `body` -> `Any`

    > Body of the request.
* *async* `put` -> `Any`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.
  * `body` -> `Any`

    > Body of the request.
* *async* `clear` -> `None`

  > Clear data in all stores.
* *async* `expire` -> `None`

  > Expire data in all the stores, used for stores that cannot automatically expire data on its own.
* *async* `delete` -> `None`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.
* *async* `contains` -> `bool`
  * `token`: `PipelineToken`

    > Token identifying the data, created by `token()` on Pyot Core objects.

## Example

```python
from pyot.conf.pipeline import pipelines
from pyot.utils.runners import loop_run

async def clear_cache_lol():
    lol_pipeline = pipelines["lol"]
    await lol_pipeline.clear()

loop_run(clear_everything())
```


# Token

### *class* `PipelineToken`

Token class is used among all the stores in the pipeline. Typically generated from `token()` on `PyotCore` objects.

Definitions:

* `__init__`
  * `model`: `str`

    > Name of the pipeline.
  * `server`: `str`

    > Name of the server (region/platform/locale).
  * `method`: `str`

    > Name of the method endpoint.
  * `params`: `Dict[str, Any]`

    > Dict containing the params.
  * `queries`: `Dict[str, Any]`

    > Dict containing the queries params.
* `__hash__` -> `str`

  > Returns the hash of the token.

Attributes:

* `value`: `str`
* `hashval`: `str`
* `model`: `str`
* `server`: `str`
* `method`: `str`
* `params`: `Dict[str, str]`
* `queries`: `Dict[str, Any]`

Methods:

* *staticmethod* `parse_params` -> `str`
  * `dic`: `Dict`
* *staticmethod* `parse_queries` -> `str`
  * `dic`: `Dict`
* *classmethod* `load` -> `PipelineToken`
  * `dic`: `Dict`
* *method* `dict` -> `Dict`


# Stores

Configuration documentations for all available stores in Pyot.

Each store is configured at pipeline definition, the configurable values of stores are the params in the `__init__` definition of each store. For example if a store can take `host` and `db` in the `__init__` method, it can be configured in the pipeline like this:

```python
    # ... Other stores
    {
        "backend": "pyot.stores.x.y",
        "host": "somehost",
        "db": "somedb"
    }
```

There are a few configurable values that exists across multiple stores, they will be documented here.

* `log_level`: `int = 0`

  > Used in all stores. Defines the log level of the logger used in the store, these uses the level specified in the [Python logging facilities](https://docs.python.org/3/library/logging.html#logging-levels), along with an extra level `0` which completely ignores the logging process. Defaults to `0`.
* `expirations`: `Dict[int, int | float | timedelta] = None`

  > Used in cache stores. Detailed documentations in **Pipeline > Expirations**.
* `error_handler`: `Dict[int, Tuple[int]] = None`

  > Used in services stores. Detailed documentations in **Pipeline > Handler**.


# CDragon

* Type: Service
* Models: `lol`, `tft`
* Description: Provides data from CommunityDragon Raw.

The CDragon has data that the game client uses. The data structure in CDragon might change at any time without warning, the Pyot Core objects for this might break aswell. Submit an issue if this happens and it will be fixed asap.

## *class* `CDragon`

Backend: `pyot.stores.cdragon.CDragon`

Definitions:

* `__init__`
  * `error_handler`: `Dict[int, Tuple] = None`
  * `log_level`: `int = 0`

## Endpoints

* `lol`
  * `cdragon_champion_by_id`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/champions/{id}.json`
  * `cdragon_champion_summary`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/champion-summary.json`
  * `cdragon_item_full`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/items.json`
  * `cdragon_rune_full`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/perks.json`
  * `cdragon_spells_full`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/summoner-spells.json`
  * `cdragon_profile_icon_full`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/profile-icons.json`
* `tft`
  * `cdragon_tft_full`: `/{version}/cdragon/tft/{locale}.json`
  * `cdragon_profile_icon_full`: `/{version}/plugins/rcp-be-lol-game-data/global/{locale}/v1/profile-icons.json`


# DDragon

* Type: Service
* Models: `lor`
* Description: Store that provides data from the Official Data Dragon.

DDragon only supports Legends of Runeterra endpoints (the only one well maintained), DDragon for LoL and TFT are not supported because they are badly maintained and considered low priority by the game team.

## *class* `DDragon`

Backend: `pyot.stores.ddragon.DDragon`

Definitions:

* `__init__`
  * `error_handler`: `Dict[int, Tuple] = None`
  * `log_level`: `int = 0`

## Endpoints

* `lor`
  * `ddragon_lor_set_data`: `/{version}/set{set}/{locale}/data/set{set}-{locale}.json`


# DiskCache

* Type: Cache
* Description: Uses Disk files (and SQLite DBs) as Caches. Takes advantage of the disk space instead of RAM.

This Cache is built on top of [diskcache](http://www.grantjenks.com/docs/diskcache/index.html) on its `FanoutCache`.

An extra installation is required: `pip install pyot[diskcache]`

## *class* `DiskCache`

Backend: `pyot.stores.diskcache.DiskCache`

Definitions:

* `__init__`
  * `directory`: `str | Path`

    > Path of the directory used as cache.
  * `expirations`: `Dict[str, int | float | timedelta] = None`
  * `log_level`: `int = 0`
  * `**kwargs`

    > Any extra kwargs provided will be passed into `diskcache.FanoutCache`.


# DjangoCache

* Type: Cache
* Description: Uses production tested caches of the Django Cache Framework.

This store is intended for projects built with Django. It can be used with **any** cache of Django. The configuration of this cache is only an alias to the configuration in Django's project settings.

## *class* `DjangoCache`

Backend: `pyot.stores.djangocache.DjangoCache`

Definitions:

* `__init__`
  * `alias`: `str`

    > The alias of the Django Cache defined in the `CACHES` variable of `settings.py` (specifically these are the dictionary keys of `CACHES`).
  * `expirations`: `Dict[str, int | float | timedelta] = None`
  * `log_level`: `int = 0`


# MerakiCDN

* Type: Service
* Models: `lol`
* Description: Provides data from the Meraki CDN.

Until now, champion abilities in particular were unavailable because ddragon's data is inaccurate and cdragon's data is unparsable, instead the data provided are collected from wiki and served by the meraki cdn.

## *class* `MerakiCDN`

Backend: `pyot.stores.merakicdn.MerakiCDN`

Definitions:

* `__init__`
  * `error_handler`: `Dict[int, Tuple] = None`
  * `log_level`: `int = 0`

## Endpoints

* `lol`
  * `meraki_champion_by_key`: `/lol/resources/latest/en-US/champions/{key}.json`
  * `meraki_item_by_id`: `/lol/resources/latest/en-US/items/{id}.json`


# MongoDB

* Type: Cache
* Description: Uses Mongo NoSQL DBs as Caches.

This store is best for production environment due to its high speed, TTL indexes and mainly disk based storage. Built on top of Python Async Driver of MongoDB [Motor](https://motor.readthedocs.io/en/stable/).

DB level sharding is possible by following the Mongo's docs for sharding and pass the necessary kwargs to the settings.

An extra installation is required: `pip install pyot[mongodb]`

## *class* `MongoDB`

Backend: `pyot.stores.mongodb.MongoDB`

Definitions:

* `__init__`
  * `db`: `str`

    > Name of the database to be used.
  * `host`: `str = '127.0.0.1'`

    > Host of the Mongo DB instance.
  * `port`: `int = 27017`

    > Port of the Mongo DB instance.
  * `expirations`: `Dict[str, int | float | timedelta] = None`
  * `log_level`: `int = 0`
  * `**kwargs`

    > Any extra kwargs provided will passed into `motor.motor_asyncio.AsyncIOMotorClient`. e.g. authentication params.


# Omnistone

* Type: Cache
* Description: In-Memory Cache that lives for the lifetime of the project runtime. Since it lives in Python memory, it's the fastest cache.

This Cache doesn't expire data after it is *expired*. To prevent memory overflow, a cull system is in place. When the amount of data reaches a limit, it calls the `expire()` coroutine on its own. If the amount of data is still higher than `MAX_ENTRIES` \* (1 - 1/`CULL_FRECUENCY`), it deletes items until its less than the limit. Deletion prioritizes least recently used data.

## *class* `Omnistone`

Backend: `pyot.stores.omnistone.Omnistone`

Definitions:

* `__init__`
  * `max_entries: int = 10000`

    > The maximum amount of items to hold before expiring
  * `cull_frecuency: int = 2`

    > The 1/x ratio of max\_entries to be culled. Manual expiring will not trigger culling.
  * `expirations`: `Dict[str, int | float | timedelta] = None`
  * `log_level`: `int = 0`


# RedisCache

* Type: Cache
* Description: Uses Redis servers as Caches. This cache provides similar speeds to Omnistone while preserving data even if the program is down.

This Cache is built on top of Async Python integration of [redis](https://github.com/redis/redis-py).

An extra installation is required: `pip install pyot[redis]`

## *class* `RedisCache`

Backend: `pyot.stores.rediscache.RedisCache`

Definitions:

* `__init__`
  * `host`: `str = '127.0.0.1'`

    > Host of Redis.
  * `port`: `int = 6379`

    > Port of Redis.
  * `db`: `int = 0`

    > Database number of Redis.
  * `expirations`: `Dict[str, int | float | timedelta] = None`
  * `log_level`: `int = 0`
  * `**kwargs`

    > Any extra kwargs provided will passed into `aioredis.Redis`. e.g username and password.


# RiotAPI

* Type: Service
* Models: `riot`, `lol`, `tft`, `lor`, `val`
* Description: Provides data from the Riot Games API, responsible for more than 80% of all endpoints.

Integrated with the official Riot Games API. Official endpoints are found at the [Riot Games Developer Portal](https://developer.riotgames.com/).

## *class* `RiotAPI`

Backend: `pyot.stores.riotapi.RiotAPI`

Definitions:

* `__init__`
  * `api_key`: `str`

    > Riot API key.
  * `rate_limiter`: `Mapping[str, str] = None`

    > Configuration of Rate Limiter for this store. Defaults to `MemoryLimiter`. Documentations at **Limiters**.
  * `error_handler`: `Dict[int, Tuple] = None`
  * `log_level`: `int = 0`

## Endpoints

* `riot` (Shared)
  * `account_v1_by_puuid`: `/riot/account/v1/accounts/by-puuid/{puuid}`
  * `account_v1_by_riot_id`: `/riot/account/v1/accounts/by-riot-id/{game_name}/{tag_line}`
  * `account_v1_active_shard`: `/riot/account/v1/active-shards/by-game/{game}/by-puuid/{puuid}`
* `lol`
  * `champion_v3_rotation`: `/lol/platform/v3/champion-rotations`
  * `champion_mastery_v4_by_champion_id`: `/lol/champion-mastery/v4/champion-masteries/by-summoner/{summoner_id}/by-champion/{champion_id}`
  * `champion_mastery_v4_all_mastery`: `/lol/champion-mastery/v4/champion-masteries/by-summoner/{summoner_id}`
  * `clash_v1_players`: `/lol/clash/v1/players/by-summoner/{summoner_id}`
  * `clash_v1_teams`: `/lol/clash/v1/teams/{id}`
  * `clash_v1_tournaments_by_team_id`: `/lol/clash/v1/tournaments/by-team/{team_id}`
  * `clash_v1_toutnaments_by_tournament_id`: `/lol/clash/v1/tournaments/{id}`
  * `clash_v1_tournaments_all`: `/lol/clash/v1/tournaments`
  * `league_v4_summoner_entries`: `/lol/league/v4/entries/by-summoner/{summoner_id}`
  * `league_v4_challenger_league`: `/lol/league/v4/challengerleagues/by-queue/{queue}`
  * `league_v4_grandmaster_league`: `/lol/league/v4/grandmasterleagues/by-queue/{queue}`
  * `league_v4_master_league`: `/lol/league/v4/masterleagues/by-queue/{queue}`
  * `league_v4_entries_by_division`: `/lol/league/v4/entries/{queue}/{tier}/{division}`
  * `league_v4_league_by_league_id`: `/lol/league/v4/leagues/{id}`
  * `status_v4_platform_data`: `/lol/status/v4/platform-data`
  * `match_v4_match`: `/lol/match/v4/matches/{id}`
  * `match_v4_timeline`: `/lol/match/v4/timelines/by-match/{id}`
  * `match_v4_matchlist`: `/lol/match/v4/matchlists/by-account/{account_id}`
  * `match_v4_tournament_match`: `/lol/match/v4/matches/{id}/by-tournament-code/{tournament_code}`
  * `match_v4_tournament_matches`: `/lol/match/v4/matches/by-tournament-code/{tournament_code}/ids`
  * `match_v5_match`: `/lol/match/v5/matches/{id}`
  * `match_v5_timeline`: `/lol/match/v5/matches/{id}/timeline`
  * `match_v5_matches`: `/lol/match/v5/matches/by-puuid/{puuid}/ids`
  * `spectator_v4_current_game`: `/lol/spectator/v4/active-games/by-summoner/{summoner_id}`
  * `spectator_v4_featured_games`: `/lol/spectator/v4/featured-games`
  * `summoner_v4_by_name`: `/lol/summoner/v4/summoners/by-name/{name}`
  * `summoner_v4_by_id`: `/lol/summoner/v4/summoners/{id}`
  * `summoner_v4_by_account_id`: `/lol/summoner/v4/summoners/by-account/{account_id}`
  * `summoner_v4_by_puuid`: `/lol/summoner/v4/summoners/by-puuid/{puuid}`
  * `third_party_code_v4_code`: `/lol/platform/v4/third-party-code/by-summoner/{summoner_id}`
  * `tournament_v4_codes`: `/lol/tournament/v4/codes`
  * `tournament_v4_codes_by_code`: `/lol/tournament/v4/codes/{code}`
  * `tournament_v4_lobby_events`: `/lol/tournament/v4/lobby-events/by-code/{code}`
  * `tournament_v4_providers`: `/lol/tournament/v4/providers`
  * `tournament_v4_tournaments`: `/lol/tournament/v4/tournaments`
  * `tournament_stub_v4_codes`: `/lol/tournament-stub/v4/codes`
  * `tournament_stub_v4_lobby_events`: `/lol/tournament-stub/v4/lobby-events/by-code/{code}`
  * `tournament_stub_v4_providers`: `/lol/tournament-stub/v4/providers`
  * `tournament_stub_v4_tournaments`: `/lol/tournament-stub/v4/tournaments`
* `tft`
  * `league_v1_summoner_entries`: `/tft/league/v1/entries/by-summoner/{summoner_id}`
  * `league_v1_challenger_league`: `/tft/league/v1/challenger`
  * `league_v1_grandmaster_league`: `/tft/league/v1/grandmaster`
  * `league_v1_master_league`: `/tft/league/v1/master`
  * `league_v1_entries_by_division`: `/tft/league/v1/entries/{tier}/{division}`
  * `league_v1_league_by_league_id`: `/tft/league/v1/leagues/{id}`
  * `match_v1_matchlist`: `/tft/match/v1/matches/by-puuid/{puuid}/ids`
  * `match_v1_match`: `/tft/match/v1/matches/{id}`
  * `summoner_v1_by_name`: `/tft/summoner/v1/summoners/by-name/{name}`
  * `summoner_v1_by_id`: `/tft/summoner/v1/summoners/{id}`
  * `summoner_v1_by_account_id`: `/tft/summoner/v1/summoners/by-account/{account_id}`
  * `summoner_v1_by_puuid`: `/tft/summoner/v1/summoners/by-puuid/{puuid}`
* `lor`
  * `ranked_v1_leaderboards`: `/lor/ranked/v1/leaderboards`
  * `match_v1_matchlist`: `/lor/match/v1/matches/by-puuid/{puuid}/ids`
  * `match_v1_match`: `/lor/match/v1/matches/{id}`
  * `status_v1_platform_data`: `/lor/status/v1/platform-data`
* `val`
  * `match_v1_match`: `/val/match/v1/matches/{id}`
  * `match_v1_matchlist`: `/val/match/v1/matchlists/by-puuid/{puuid}`
  * `match_v1_recent`: `/val/match/v1/recent-matches/by-queue/{queue}`
  * `content_v1_contents`: `/val/content/v1/contents`
  * `ranked_v1_leaderboards`: `/val/ranked/v1/leaderboards/by-act/{act_id}`
  * `status_v1_platform_data`: `/val/status/v1/platform-data`


# Limiters

Rate limiting is an essential part of using the Riot Games API.

* Prevents requests hitting 429.
* Lowers the risk of being banned due to excessive 429s.

They are only used in the RiotAPI store, different rate limiters has different pros and cons.

Rate limiters are configured on the `rate_limiter` param of the store configuration, the configurable values of rate limiters are params in the `__init__` definition of each rate limiter. Similar to how stores are configured.

There are a few configurable values that exists across multiple rate limiters, they will be documented here.

* `limiting_share`: `float = 1`

  > Value from 0 to 1. Rate limiter will only allow requests up to `bucket_max * limiting_share` (e.g. 0.7 will result in only using 70% of the limit).

## Example

```python
    # ... Other stores
    {
        "backend": "pyot.stores.riotapi.RiotAPI",
        "api_key": os.environ["RIOT_API_KEY"],
        "rate_limiter": {
            "backend": "pyot.limiters.redis.RedisLimiter",
            "limiting_share": 1,
            "host": "127.0.0.1",
            "port": 6379,
            "db": 0,
        }
    }
```


# MemoryLimiter

* Description: In-Memory rate limiter that lives for the lifetime of the project runtime.

Pros:

* Fastest rate limiter since it lives in Python memory.
* No extra dependencies.

Cons:

* It makes the project stateful, meaning the rate limiter internal state is unique for each process running the project. If there are more than 1 process running the project, this rate limiter will fail.
* Rate limiter state is lost when the process is stopped or restarted.

## *class* `MemoryLimiter`

Backend: `pyot.limiters.memory.MemoryLimiter`

Definitions:

* `__init__`
  * `limiting_share`: `float = 1`


# RedisLimiter

* Description: Redis based rate limiter.

Pros:

* Redis is a relatively fast key-value storage.
* Stateless, the state is stored in a redis server that can be accessed across processes.

Cons:

* Although minimal, latency exists when the redis server is on another machine.

An extra installation is required: `pip install pyot[redis]`

## *class* `RedisLimiter`

Backend: `pyot.limiters.redis.RedisLimiter`

Definitions:

* `__init__`
  * `host`: `str = '127.0.0.1'`

    > Host of Redis.
  * `port`: `int = 6379`

    > Port of Redis.
  * `db`: `int = 0`

    > Database number of Redis.
  * `limiting_share`: `float = 1`
  * `**kwargs`

    > Any extra kwargs provided will passed into `aioredis.Redis`. e.g. username and password.


# Models


# League of Legends

## Routing Regions

* `americas`
* `asia`
* `esports`
* `europe`
* `sea`

## Routing Platforms

* `br1`
* `eun1`
* `euw1`
* `jp1`
* `kr`
* `la1`
* `la2`
* `na1`
* `oc1`
* `ru`
* `tr1`
* `ph2`
* `sg2`
* `th2`
* `tw2`
* `vn2`


# Champion

Module: `pyot.models.lol.champion`

### *class* `Champion`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `key`: `str = empty`
  * `name`: `str = empty`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_champion_by_id`: `['version', 'locale', 'id']`

Attributes:

* `id` -> `int`
* `key` -> `str`
* `name` -> `str`
* `lore` -> `str`
* `tactical_info` -> `pyot.models.lol.champion.ChampionTacticalData`
* `play_style` -> `pyot.models.lol.champion.ChampionPlayerStyleData`
* `square_path` -> `str`
* `stinger_sfx_path` -> `str`
* `choose_vo_path` -> `str`
* `ban_vo_path` -> `str`
* `roles` -> `List[str]`
* `skins` -> `List[pyot.models.lol.champion.ChampionSkinData]`
* `abilities` -> `pyot.models.lol.champion.ChampionAbilityData`
* `passive` -> `pyot.models.lol.champion.ChampionPassiveData`
* `title` -> `str`
* `recommended_item_defaults` -> `List[str]`

Properties:

* *property* `meraki_champion` -> `MerakiChampion`

### *class* `Champions`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lol.champion.Champion]`
* `__len__` -> `int`

Endpoints:

* `cdragon_champion_summary`: `['version', 'locale']`

Attributes:

* `champions` -> `List[pyot.models.lol.champion.Champion]`

### *class* `ChampionAbilityData`

Type: `PyotStatic`

Attributes:

* `p` -> `pyot.models.lol.champion.ChampionPassiveData`
* `q` -> `pyot.models.lol.champion.ChampionSpellData`
* `w` -> `pyot.models.lol.champion.ChampionSpellData`
* `e` -> `pyot.models.lol.champion.ChampionSpellData`
* `r` -> `pyot.models.lol.champion.ChampionSpellData`

### *class* `ChampionChromaDescriptionsData`

Type: `PyotStatic`

Attributes:

* `region` -> `str`
* `description` -> `str`

### *class* `ChampionChromaRaritiesData`

Type: `PyotStatic`

Attributes:

* `region` -> `str`
* `description` -> `str`
* `rarity` -> `int`

### *class* `ChampionPassiveData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `icon_path` -> `str`
* `description` -> `str`
* `ability_video_path` -> `str`
* `ability_video_image_path` -> `str`

### *class* `ChampionPlayerStyleData`

Type: `PyotStatic`

Attributes:

* `damage` -> `int`
* `durability` -> `int`
* `crowd_control` -> `int`
* `mobility` -> `int`
* `utility` -> `int`

### *class* `ChampionSkinChromaData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `name` -> `str`
* `chroma_path` -> `str`
* `colors` -> `List[str]`
* `descriptions` -> `List[pyot.models.lol.champion.ChampionChromaDescriptionsData]`
* `rarities` -> `List[pyot.models.lol.champion.ChampionChromaRaritiesData]`

### *class* `ChampionSkinData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `is_base` -> `bool`
* `name` -> `str`
* `splash_path` -> `str`
* `uncentered_splash_path` -> `str`
* `tile_path` -> `str`
* `load_screen_path` -> `str`
* `load_screen_vintage_path` -> `str`
* `skin_type` -> `str`
* `rarity` -> `str`
* `is_legacy` -> `bool`
* `chroma_path` -> `str`
* `chromas` -> `List[pyot.models.lol.champion.ChampionSkinChromaData]`
* `emblems` -> `List[str]`
* `skin_line` -> `int`
* `description` -> `str`
* `splash_video_path` -> `str`
* `collection_splash_video_path` -> `str`
* `features_text` -> `str`
* `region_rarity_id` -> `int`
* `rarity_gem_path` -> `str`

### *class* `ChampionSpellData`

Type: `PyotStatic`

Attributes:

* `key` -> `str`
* `name` -> `str`
* `icon_path` -> `str`
* `cost` -> `List[float]`
* `cooldown` -> `List[float]`
* `range` -> `List[float]`
* `description` -> `str`
* `long_description` -> `str`
* `ability_video_path` -> `str`
* `ability_video_image_path` -> `str`
* `max_level` -> `int`
* `formulas` -> `Dict`
* `coefficients` -> `Dict[str, float]`
* `effect_amounts` -> `Dict[str, List[float]]`
* `ammo` -> `Dict[str, List[float]]`

### *class* `ChampionTacticalData`

Type: `PyotStatic`

Attributes:

* `style` -> `int`
* `difficulty` -> `int`
* `damage_type` -> `str`


# Championmastery

Module: `pyot.models.lol.championmastery`

### *class* `ChampionMasteries`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.lol.championmastery.ChampionMastery]`
* `__len__` -> `int`

Endpoints:

* `champion_mastery_v4_all_mastery`: `['summoner_id']`

Attributes:

* `summoner_id` -> `str`
* `masteries` -> `List[pyot.models.lol.championmastery.ChampionMastery]`
* `total_score` -> `int`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `ChampionMastery`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `champion_id`: `int = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `champion_mastery_v4_by_champion_id`: `['summoner_id', 'champion_id']`

Attributes:

* `champion_id` -> `int`
* `champion_level` -> `int`
* `champion_points` -> `int`
* `last_play_timestamp` -> `int`
* `champion_points_since_last_level` -> `int`
* `champion_points_until_next_level` -> `int`
* `chest_granted` -> `bool`
* `tokens_earned` -> `int`
* `summoner_id` -> `str`

Properties:

* *property* `champion` -> `Champion`
* *property* `last_play_time` -> `datetime.datetime`
* *property* `meraki_champion` -> `MerakiChampion`
* *property* `summoner` -> `Summoner`


# Championrotation

Module: `pyot.models.lol.championrotation`

### *class* `ChampionRotation`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `champion_v3_rotation`: `[]`

Attributes:

* `free_champion_ids` -> `List[int]`
* `free_newie_champion_ids` -> `List[int]`
* `newie_max_level` -> `int`

Properties:

* *property* `free_champions` -> `List[ForwardRef(Champion)]`
* *property* `free_newie_champions` -> `List[ForwardRef(Champion)]`
* *property* `meraki_free_champions` -> `List[ForwardRef(MerakiChampion)]`
* *property* `meraki_free_newie_champions` -> `List[ForwardRef(MerakiChampion)]`


# Clash

Module: `pyot.models.lol.clash`

### *class* `ClashPlayers`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `clash_v1_players`: `['summoner_id']`

Attributes:

* `summoner_id` -> `str`
* `players` -> `List[pyot.models.lol.clash.ClashPlayerData]`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `ClashTeam`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `clash_v1_teams`: `['id']`

Attributes:

* `id` -> `str`
* `tournament_id` -> `int`
* `name` -> `str`
* `icon_id` -> `int`
* `tier` -> `int`
* `captain_summoner_id` -> `str`
* `abbreviation` -> `str`
* `players` -> `List[pyot.models.lol.clash.ClashPlayerData]`

Properties:

* *property* `captain` -> `Summoner`
* *property* `tournament` -> `ClashTournament`

### *class* `ClashTournament`

Type: `PyotCore`

Extends:

* `pyot.models.lol.clash.ClashTournamentData`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `team_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `clash_v1_tournaments_by_team_id`: `['team_id']`
* `clash_v1_toutnaments_by_tournament_id`: `['id']`

Attributes:

* `id` -> `int`
* `theme_id` -> `int`
* `name_key` -> `str`
* `name_key_secondary` -> `str`
* `schedule` -> `List[pyot.models.lol.clash.ClashTournamentPhaseData]`
* `team_id` -> `str`

Properties:

* *property* `team` -> `pyot.models.lol.clash.ClashTeam`

### *class* `ClashTournaments`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`
* `__iter__` -> `List[pyot.models.lol.clash.ClashTournamentData]`
* `__len__` -> `int`

Endpoints:

* `clash_v1_tournaments_all`: `[]`

Attributes:

* `tournaments` -> `List[pyot.models.lol.clash.ClashTournamentData]`

### *class* `ClashPlayerData`

Type: `PyotStatic`

Attributes:

* `summoner_id` -> `str`
* `team_id` -> `str`
* `position` -> `str`
* `role` -> `str`

Properties:

* *property* `summoner` -> `Summoner`
* *property* `team` -> `ClashTeam`

### *class* `ClashTournamentData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `theme_id` -> `int`
* `name_key` -> `str`
* `name_key_secondary` -> `str`
* `schedule` -> `List[pyot.models.lol.clash.ClashTournamentPhaseData]`

### *class* `ClashTournamentPhaseData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `registration_timestamp` -> `int`
* `start_timestamp` -> `int`
* `cancelled` -> `bool`

Properties:

* *property* `registration_time` -> `datetime.datetime`
* *property* `start_time` -> `datetime.datetime`


# Item

Module: `pyot.models.lol.item`

### *class* `Item`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_item_full`: `['version', 'locale', '?id']`

Attributes:

* `id` -> `int`
* `name` -> `str`
* `description` -> `str`
* `active` -> `bool`
* `in_store` -> `bool`
* `from_ids` -> `List[int]`
* `to_ids` -> `List[int]`
* `categories` -> `List[str]`
* `maps` -> `List[str]`
* `max_stacks` -> `int`
* `modes` -> `List[str]`
* `required_champion_key` -> `str`
* `required_ally` -> `str`
* `required_currency` -> `str`
* `required_currency_cost` -> `int`
* `is_enchantment` -> `bool`
* `special_recipe_id` -> `int`
* `self_cost` -> `int`
* `total_cost` -> `int`
* `icon_path` -> `str`

Properties:

* *property* `from_items` -> `List[ForwardRef(Item)]`
* *property* `meraki_item` -> `MerakiItem`
* *property* `required_champion` -> `Champion`
* *property* `special_recipe` -> `Item`
* *property* `to_items` -> `List[ForwardRef(Item)]`

### *class* `Items`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lol.item.Item]`
* `__len__` -> `int`

Endpoints:

* `cdragon_item_full`: `['version', 'locale']`

Attributes:

* `items` -> `List[pyot.models.lol.item.Item]`


# League

Module: `pyot.models.lol.league`

### *class* `ApexLeague`

Type: `PyotCore`

Extends:

* `pyot.models.lol.league.League`

Definitions:

* `__init__` -> `None`
  * `queue`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `league_v4_league_by_league_id`: `['id']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.lol.league.LeagueEntryData]`

Properties:

* *property* `league` -> `pyot.models.lol.league.League`

### *class* `ChallengerLeague`

Type: `PyotCore`

Extends:

* `pyot.models.lol.league.ApexLeague`
* `pyot.models.lol.league.League`

Definitions:

Endpoints:

* `league_v4_challenger_league`: `['queue']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.lol.league.LeagueEntryData]`

### *class* `DivisionLeague`

Type: `PyotCore`

Extends:

* `pyot.models.lol.league.SummonerLeague`

Definitions:

* `__init__` -> `None`
  * `queue`: `str = empty`
  * `division`: `str = empty`
  * `tier`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `league_v4_entries_by_division`: `['queue', 'tier', 'division']`

Query Params:

* `page`: `int = empty`

Attributes:

* `summoner_id` -> `str`
* `entries` -> `List[pyot.models.lol.league.SummonerLeagueEntryData]`
* `queue` -> `str`
* `division` -> `str`
* `tier` -> `str`

Properties:

* *property* `summoner` -> `NoReturn`

### *class* `GrandmasterLeague`

Type: `PyotCore`

Extends:

* `pyot.models.lol.league.ApexLeague`
* `pyot.models.lol.league.League`

Definitions:

Endpoints:

* `league_v4_grandmaster_league`: `['queue']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.lol.league.LeagueEntryData]`

### *class* `League`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `league_v4_league_by_league_id`: `['id']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.lol.league.LeagueEntryData]`

### *class* `MasterLeague`

Type: `PyotCore`

Extends:

* `pyot.models.lol.league.ApexLeague`
* `pyot.models.lol.league.League`

Definitions:

Endpoints:

* `league_v4_master_league`: `['queue']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.lol.league.LeagueEntryData]`

### *class* `SummonerLeague`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.lol.league.SummonerLeagueEntryData]`
* `__len__` -> `int`

Endpoints:

* `league_v4_summoner_entries`: `['summoner_id']`

Attributes:

* `summoner_id` -> `str`
* `entries` -> `List[pyot.models.lol.league.SummonerLeagueEntryData]`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `LeagueEntryData`

Type: `PyotStatic`

Attributes:

* `summoner_id` -> `str`
* `summoner_name` -> `str`
* `league_points` -> `int`
* `rank` -> `str`
* `wins` -> `int`
* `losses` -> `int`
* `veteran` -> `bool`
* `inactive` -> `bool`
* `fresh_blood` -> `bool`
* `hot_streak` -> `bool`
* `mini_series` -> `pyot.models.lol.league.MiniSeriesData`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `MiniSeriesData`

Type: `PyotStatic`

Attributes:

* `target` -> `int`
* `wins` -> `int`
* `losses` -> `int`
* `progress` -> `str`

### *class* `SummonerLeagueEntryData`

Type: `PyotStatic`

Extends:

* `pyot.models.lol.league.LeagueEntryData`

Attributes:

* `summoner_id` -> `str`
* `summoner_name` -> `str`
* `league_points` -> `int`
* `rank` -> `str`
* `wins` -> `int`
* `losses` -> `int`
* `veteran` -> `bool`
* `inactive` -> `bool`
* `fresh_blood` -> `bool`
* `hot_streak` -> `bool`
* `mini_series` -> `pyot.models.lol.league.MiniSeriesData`
* `league_id` -> `str`
* `queue` -> `str`
* `tier` -> `str`

Properties:

* *property* `league` -> `League`


# Match

Module: `pyot.models.lol.match`

### *class* `Match`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `region`: `str = models.lol.DEFAULT_REGION`

Endpoints:

* `match_v5_match`: `['id']`

Methods:

* *method* `feed_timeline` -> `None`

  * `timeline`: `Timeline`

  * `include_assisted`: `bool = False`

  * `include_victim`: `bool = False`

  > Parse the given `Timeline` object's frames and events into this match's participants.
  >
  > * `include_assisted`: Include frames and events where the participants are scoring an assist.
  > * `include_victim`: Include frames and events where the participants are victims.

Attributes:

* `metadata` -> `pyot.models.lol.match.MatchMetaData`
* `info` -> `pyot.models.lol.match.MatchInfoData`
* `id` -> `str`

### *class* `MatchHistory`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `region`: `str = models.lol.DEFAULT_REGION`
* `__iter__` -> `Iterator[pyot.models.lol.match.Match]`
* `__len__` -> `int`

Endpoints:

* `match_v5_matches`: `['puuid']`

Query Params:

* `start`: `int = 0`
* `count`: `int = 20`
* `queue`: `int = empty`
* `type`: `str = empty`
* `start_time`: `Union[int, datetime.datetime] = empty`
* `end_time`: `Union[int, datetime.datetime] = empty`

Attributes:

* `ids` -> `List[str]`
* `puuid` -> `str`

Properties:

* *property* `match_timelines` -> `List[Tuple[pyot.models.lol.match.Match, pyot.models.lol.match.Timeline]]`
* *property* `matches` -> `List[pyot.models.lol.match.Match]`
* *property* `timelines` -> `List[pyot.models.lol.match.Timeline]`

### *class* `Timeline`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `region`: `str = models.lol.DEFAULT_REGION`

Endpoints:

* `match_v5_timeline`: `['id']`

Attributes:

* `metadata` -> `pyot.models.lol.match.MatchMetaData`
* `info` -> `pyot.models.lol.match.TimelineInfoData`
* `id` -> `str`

### *class* `MatchBanData`

Type: `PyotStatic`

Attributes:

* `champion_id` -> `int`
* `pick_turn` -> `int`

Properties:

* *property* `champion` -> `Champion`
* *property* `meraki_champion` -> `MerakiChampion`

### *class* `MatchInfoData`

Type: `PyotStatic`

Attributes:

* `game_id` -> `int`
* `creation_millis` -> `int`
* `duration_units` -> `int`
* `start_millis` -> `int`
* `end_millis` -> `int`
* `mode` -> `str`
* `name` -> `str`
* `type` -> `str`
* `version` -> `str`
* `platform` -> `str`
* `map_id` -> `int`
* `queue_id` -> `int`
* `tournament_code` -> `str`
* `participants` -> `List[pyot.models.lol.match.MatchParticipantData]`
* `teams` -> `List[pyot.models.lol.match.MatchTeamData]`

Properties:

* *property* `creation` -> `datetime.datetime`
* *property* `duration` -> `datetime.timedelta`
* *property* `duration_millis` -> `int`
* *property* `duration_secs` -> `int`
* *property* `end` -> `datetime.datetime`
* *property* `start` -> `datetime.datetime`

### *class* `MatchMetaData`

Type: `PyotStatic`

Attributes:

* `match_id` -> `str`
* `data_version` -> `str`
* `participant_puuids` -> `List[str]`

Properties:

* *property* `participants` -> `Summoner`

### *class* `MatchObjectiveData`

Type: `PyotStatic`

Attributes:

* `baron` -> `pyot.models.lol.match.MatchObjectiveDetailData`
* `champion` -> `pyot.models.lol.match.MatchObjectiveDetailData`
* `dragon` -> `pyot.models.lol.match.MatchObjectiveDetailData`
* `inhibitor` -> `pyot.models.lol.match.MatchObjectiveDetailData`
* `rift_herald` -> `pyot.models.lol.match.MatchObjectiveDetailData`
* `tower` -> `pyot.models.lol.match.MatchObjectiveDetailData`

### *class* `MatchObjectiveDetailData`

Type: `PyotStatic`

Attributes:

* `first` -> `bool`
* `kills` -> `int`

### *class* `MatchParticipantData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `assists` -> `int`
* `baron_kills` -> `int`
* `basic_pings` -> `int`
* `bounty_level` -> `int`
* `champ_experience` -> `int`
* `champ_level` -> `int`
* `champion_id` -> `int`
* `champion_name` -> `str`
* `champion_transform` -> `int`
* `consumables_purchased` -> `int`
* `damage_dealt_to_buildings` -> `int`
* `damage_dealt_to_objectives` -> `int`
* `damage_dealt_to_turrets` -> `int`
* `damage_self_mitigated` -> `int`
* `deaths` -> `int`
* `detector_wards_placed` -> `int`
* `double_kills` -> `int`
* `dragon_kills` -> `int`
* `eligible_for_progression` -> `bool`
* `first_blood_assist` -> `bool`
* `first_blood_kill` -> `bool`
* `first_tower_assist` -> `bool`
* `first_tower_kill` -> `bool`
* `game_ended_in_early_surrender` -> `bool`
* `game_ended_in_surrender` -> `bool`
* `gold_earned` -> `int`
* `gold_spent` -> `int`
* `individual_position` -> `str`
* `inhibitor_kills` -> `int`
* `inhibitor_takedowns` -> `int`
* `inhibitors_lost` -> `int`
* `item0` -> `int`
* `item1` -> `int`
* `item2` -> `int`
* `item3` -> `int`
* `item4` -> `int`
* `item5` -> `int`
* `item6` -> `int`
* `items_purchased` -> `int`
* `killing_sprees` -> `int`
* `kills` -> `int`
* `lane` -> `str`
* `largest_critical_strike` -> `int`
* `largest_killing_spree` -> `int`
* `largest_multi_kill` -> `int`
* `longest_time_spent_living_secs` -> `int`
* `magic_damage_dealt` -> `int`
* `magic_damage_dealt_to_champions` -> `int`
* `magic_damage_taken` -> `int`
* `neutral_minions_killed` -> `int`
* `nexus_kills` -> `int`
* `nexus_takedowns` -> `int`
* `nexus_lost` -> `int`
* `objectives_stolen` -> `int`
* `objectives_stolen_assists` -> `int`
* `penta_kills` -> `int`
* `perks` -> `pyot.models.lol.match.MatchPerkData`
* `physical_damage_dealt` -> `int`
* `physical_damage_dealt_to_champions` -> `int`
* `physical_damage_taken` -> `int`
* `profile_icon_id` -> `int`
* `puuid` -> `str`
* `quadra_kills` -> `int`
* `riot_id_name` -> `str`
* `riot_id_tagline` -> `str`
* `role` -> `str`
* `sight_wards_bought_in_game` -> `int`
* `spell1_casts` -> `int`
* `spell2_casts` -> `int`
* `spell3_casts` -> `int`
* `spell4_casts` -> `int`
* `summoner1_casts` -> `int`
* `summoner1_id` -> `int`
* `summoner2_casts` -> `int`
* `summoner2_id` -> `int`
* `summoner_id` -> `str`
* `summoner_level` -> `int`
* `summoner_name` -> `str`
* `team_early_surrendered` -> `bool`
* `team_id` -> `int`
* `team_position` -> `str`
* `time_ccing_others_secs` -> `int`
* `time_played_secs` -> `int`
* `total_damage_dealt` -> `int`
* `total_damage_dealt_to_champions` -> `int`
* `total_damage_shielded_on_teammates` -> `int`
* `total_damage_taken` -> `int`
* `total_heal` -> `int`
* `total_heals_on_teammates` -> `int`
* `total_minions_killed` -> `int`
* `total_time_cc_dealt_secs` -> `int`
* `total_time_spent_dead_secs` -> `int`
* `total_units_healed` -> `int`
* `triple_kills` -> `int`
* `true_damage_dealt` -> `int`
* `true_damage_dealt_to_champions` -> `int`
* `true_damage_taken` -> `int`
* `turret_kills` -> `int`
* `turret_takedowns` -> `int`
* `turrets_lost` -> `int`
* `unreal_kills` -> `int`
* `vision_score` -> `int`
* `vision_wards_bought_in_game` -> `int`
* `wards_killed` -> `int`
* `wards_placed` -> `int`
* `challenges` -> `Dict[str, float]`
* `frames` -> `List[pyot.models.lol.match.TimelineParticipantFrameData]`
* `events` -> `List[pyot.models.lol.match.TimelineEventData]`
* `win` -> `bool`

Properties:

* *property* `items` -> `List[ForwardRef(Item)]`
* *property* `longest_time_spent_living` -> `datetime.timedelta`
* *property* `meraki_items` -> `List[ForwardRef(MerakiItem)]`
* *property* `runes` -> `List[ForwardRef(Rune)]`
* *property* `spells` -> `List[ForwardRef(Spell)]`
* *property* `summoner` -> `Summoner`
* *property* `time_ccing_others` -> `datetime.timedelta`
* *property* `time_played` -> `datetime.timedelta`
* *property* `total_time_cc_dealt` -> `datetime.timedelta`
* *property* `total_time_spent_dead` -> `datetime.timedelta`

### *class* `MatchPerkData`

Type: `PyotStatic`

Attributes:

* `stat_perks` -> `pyot.models.lol.match.MatchStatPerkData`
* `styles` -> `List[pyot.models.lol.match.MatchPerkStyleData]`

### *class* `MatchPerkSelectionData`

Type: `PyotStatic`

Attributes:

* `perk` -> `int`
* `var1` -> `int`
* `var2` -> `int`
* `var3` -> `int`

### *class* `MatchPerkStyleData`

Type: `PyotStatic`

Attributes:

* `description` -> `str`
* `selections` -> `List[pyot.models.lol.match.MatchPerkSelectionData]`
* `style` -> `int`

### *class* `MatchStatPerkData`

Type: `PyotStatic`

Attributes:

* `offense` -> `int`
* `flex` -> `int`
* `defense` -> `int`

### *class* `MatchTeamData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `win` -> `bool`
* `bans` -> `List[pyot.models.lol.match.MatchBanData]`
* `objectives` -> `pyot.models.lol.match.MatchObjectiveData`

Properties:

* *property* `participants` -> `List[ForwardRef(MatchParticipantData)]`

### *class* `TimelineChampionStatData`

Type: `PyotStatic`

Attributes:

* `ability_haste` -> `int`
* `ability_power` -> `int`
* `armor` -> `int`
* `armor_pen` -> `int`
* `armor_pen_percent` -> `int`
* `attack_damage` -> `int`
* `attack_speed` -> `int`
* `bonus_armor_pen_percent` -> `int`
* `bonus_magic_pen_percent` -> `int`
* `cc_reduction` -> `int`
* `cooldown_reduction` -> `int`
* `health` -> `int`
* `health_max` -> `int`
* `health_regen` -> `int`
* `lifesteal` -> `int`
* `magic_pen` -> `int`
* `magic_pen_percent` -> `int`
* `magic_resist` -> `int`
* `movement_speed` -> `int`
* `omnivamp` -> `int`
* `physical_vamp` -> `int`
* `power` -> `int`
* `power_max` -> `int`
* `power_regen` -> `int`
* `spell_vamp` -> `int`

### *class* `TimelineDamageStatData`

Type: `PyotStatic`

Attributes:

* `magic_damage_done` -> `int`
* `magic_damage_done_to_champions` -> `int`
* `magic_damage_taken` -> `int`
* `physical_damage_done` -> `int`
* `physical_damage_done_to_champions` -> `int`
* `physical_damage_taken` -> `int`
* `total_damage_done` -> `int`
* `total_damage_done_to_champions` -> `int`
* `total_damage_taken` -> `int`
* `true_damage_done` -> `int`
* `true_damage_done_to_champions` -> `int`
* `true_damage_taken` -> `int`

### *class* `TimelineEventData`

Type: `PyotStatic`

Attributes:

* `actual_start_time_millis` -> `int`
* `ascended_type` -> `str`
* `assisting_participant_ids` -> `List[int]`
* `after_id` -> `int`
* `before_id` -> `int`
* `bounty` -> `int`
* `building_type` -> `str`
* `creator_id` -> `int`
* `event_type` -> `str`
* `game_id` -> `int`
* `gold_gain` -> `int`
* `item_id` -> `int`
* `kill_streak_length` -> `int`
* `kill_type` -> `str`
* `killer_id` -> `int`
* `killer_team_id` -> `int`
* `lane_type` -> `str`
* `level` -> `int`
* `level_up_type` -> `str`
* `monster_type` -> `str`
* `monster_sub_type` -> `str`
* `multi_kill_length` -> `int`
* `name` -> `str`
* `participant_id` -> `int`
* `point_captured` -> `str`
* `position` -> `pyot.models.lol.match.TimelinePositionData`
* `real_timestamp` -> `int`
* `skill_slot` -> `int`
* `shutdown_bounty` -> `int`
* `team_id` -> `int`
* `timestamp` -> `int`
* `transform_type` -> `str`
* `type` -> `str`
* `tower_type` -> `str`
* `victim_id` -> `int`
* `victim_damage_dealt` -> `List[pyot.models.lol.match.TimelineVictimDamageData]`
* `victim_damage_received` -> `List[pyot.models.lol.match.TimelineVictimDamageData]`
* `ward_type` -> `str`
* `winning_team` -> `int`

Properties:

* *property* `actual_start_time` -> `datetime.timedelta`
* *property* `after_item` -> `Item`
* *property* `before_item` -> `Item`
* *property* `item` -> `Item`
* *property* `meraki_after_item` -> `MerakiItem`
* *property* `meraki_before_item` -> `MerakiItem`
* *property* `meraki_item` -> `MerakiItem`
* *property* `real_time` -> `datetime.datetime`
* *property* `time` -> `datetime.timedelta`

### *class* `TimelineFrameData`

Type: `PyotStatic`

Attributes:

* `events` -> `List[pyot.models.lol.match.TimelineEventData]`
* `participant_frames` -> `List[pyot.models.lol.match.TimelineParticipantFrameData]`
* `timestamp` -> `int`

Properties:

* *property* `time` -> `datetime.timedelta`

### *class* `TimelineInfoData`

Type: `PyotStatic`

Attributes:

* `frame_interval_millis` -> `int`
* `frames` -> `List[pyot.models.lol.match.TimelineFrameData]`
* `game_id` -> `int`
* `participants` -> `List[pyot.models.lol.match.TimelineParticipantData]`

Properties:

* *property* `frame_interval` -> `datetime.timedelta`

### *class* `TimelineParticipantData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `puuid` -> `str`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `TimelineParticipantFrameData`

Type: `PyotStatic`

Attributes:

* `champion_stats` -> `pyot.models.lol.match.TimelineChampionStatData`
* `current_gold` -> `int`
* `damage_stats` -> `pyot.models.lol.match.TimelineDamageStatData`
* `gold_per_second` -> `int`
* `jungle_minions_killed` -> `int`
* `level` -> `int`
* `minions_killed` -> `int`
* `participant_id` -> `int`
* `position` -> `pyot.models.lol.match.TimelinePositionData`
* `time_enemy_spent_controlled_millis` -> `int`
* `total_gold` -> `int`
* `xp` -> `int`

Properties:

* *property* `time_enemy_spent_controlled` -> `datetime.timedelta`

### *class* `TimelinePositionData`

Type: `PyotStatic`

Attributes:

* `x` -> `int`
* `y` -> `int`

### *class* `TimelineVictimDamageData`

Type: `PyotStatic`

Attributes:

* `basic` -> `bool`
* `magic_damage` -> `int`
* `name` -> `str`
* `participant_id` -> `int`
* `physical_damage` -> `int`
* `spell_name` -> `str`
* `spell_slot` -> `int`
* `true_damage` -> `int`
* `type` -> `str`


# Merakichampion

Module: `pyot.models.lol.merakichampion`

### *class* `MerakiChampion`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `key`: `str = empty`
  * `name`: `str = empty`

Endpoints:

* `meraki_champion_by_key`: `['key']`

Attributes:

* `id` -> `int`
* `key` -> `str`
* `name` -> `str`
* `title` -> `str`
* `full_name` -> `str`
* `icon` -> `str`
* `resource` -> `str`
* `attack_type` -> `str`
* `adaptive_type` -> `str`
* `stats` -> `pyot.models.lol.merakichampion.MerakiChampionStatData`
* `roles` -> `List[str]`
* `attribute_ratings` -> `pyot.models.lol.merakichampion.MerakiChampionAttributeRatingData`
* `abilities` -> `pyot.models.lol.merakichampion.MerakiChampionAbilityData`
* `release_date` -> `str`
* `release_patch` -> `str`
* `patch_last_changed` -> `str`
* `price` -> `pyot.models.lol.merakichampion.MerakiChampionPriceData`
* `skins` -> `List[pyot.models.lol.merakichampion.MerakiChampionSkinData]`
* `lore` -> `str`

Properties:

* *property* `champion` -> `Champion`

### *class* `MerakiChampionAbilityData`

Type: `PyotStatic`

Attributes:

* `p` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellData]`
* `q` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellData]`
* `w` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellData]`
* `e` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellData]`
* `r` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellData]`

### *class* `MerakiChampionAttributeRatingData`

Type: `PyotStatic`

Attributes:

* `damage` -> `int`
* `toughness` -> `int`
* `control` -> `int`
* `mobility` -> `int`
* `utility` -> `int`
* `ability_reliance` -> `int`
* `attack` -> `int`
* `defense` -> `int`
* `magic` -> `int`
* `difficulty` -> `int`

### *class* `MerakiChampionChromaDescriptionsData`

Type: `PyotStatic`

Attributes:

* `region` -> `str`
* `description` -> `str`

### *class* `MerakiChampionChromaRaritiesData`

Type: `PyotStatic`

Attributes:

* `region` -> `str`
* `description` -> `str`
* `rarity` -> `int`

### *class* `MerakiChampionPriceData`

Type: `PyotStatic`

Attributes:

* `blue_essence` -> `int`
* `rp` -> `int`
* `sale_rp` -> `int`

### *class* `MerakiChampionSkinChromaData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `name` -> `str`
* `chroma_path` -> `str`
* `colors` -> `List[str]`
* `descriptions` -> `List[pyot.models.lol.merakichampion.MerakiChampionChromaDescriptionsData]`
* `rarities` -> `List[pyot.models.lol.merakichampion.MerakiChampionChromaRaritiesData]`

### *class* `MerakiChampionSkinData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `id` -> `int`
* `is_base` -> `bool`
* `availability` -> `str`
* `format_name` -> `str`
* `loot_eligible` -> `bool`
* `cost` -> `Union[str, int]`
* `sale` -> `int`
* `distribution` -> `str`
* `rarity` -> `str`
* `chromas` -> `List[pyot.models.lol.merakichampion.MerakiChampionSkinChromaData]`
* `lore` -> `str`
* `release` -> `str`
* `set` -> `List[str]`
* `splash_path` -> `str`
* `uncentered_splash_path` -> `str`
* `tile_path` -> `str`
* `load_screen_path` -> `str`
* `load_screen_vintage_path` -> `str`
* `new_effects` -> `bool`
* `new_animations` -> `bool`
* `new_recall` -> `bool`
* `new_voice` -> `bool`
* `new_quotes` -> `bool`
* `voice_actor` -> `List[str]`
* `splash_artist` -> `List[str]`

### *class* `MerakiChampionSpellAttrData`

Type: `PyotStatic`

Attributes:

* `attribute` -> `str`
* `modifiers` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellModifierData]`
* `affected_by_cdr` -> `bool`

### *class* `MerakiChampionSpellData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `icon` -> `str`
* `effects` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellEffectData]`
* `cost` -> `pyot.models.lol.merakichampion.MerakiChampionSpellAttrData`
* `cooldown` -> `pyot.models.lol.merakichampion.MerakiChampionSpellAttrData`
* `targeting` -> `str`
* `affects` -> `str`
* `spellshieldable` -> `str`
* `resource` -> `str`
* `damage_type` -> `str`
* `spell_effects` -> `str`
* `projectile` -> `str`
* `on_hit_effects` -> `str`
* `occurrence` -> `int`
* `notes` -> `str`
* `blurb` -> `str`
* `missile_speed` -> `str`
* `recharge_rate` -> `str`
* `collision_radius` -> `str`
* `tether_radius` -> `str`
* `on_target_cd_static` -> `str`
* `inner_radius` -> `str`
* `speed` -> `str`
* `width` -> `str`
* `angle` -> `str`
* `cast_time` -> `str`
* `effect_radius` -> `str`
* `target_range` -> `str`

### *class* `MerakiChampionSpellEffectData`

Type: `PyotStatic`

Attributes:

* `description` -> `str`
* `leveling` -> `List[pyot.models.lol.merakichampion.MerakiChampionSpellAttrData]`

### *class* `MerakiChampionSpellModifierData`

Type: `PyotStatic`

Attributes:

* `values` -> `List[float]`
* `units` -> `List[str]`

### *class* `MerakiChampionStatData`

Type: `PyotStatic`

Attributes:

* `health` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `health_regen` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `mana` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `mana_regen` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `armor` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `magic_resistance` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_damage` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `movespeed` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `acquisition_radius` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `selection_radius` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `pathing_radius` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `gameplay_radius` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `critical_strike_damage` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `critical_strike_damage_modifier` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_speed` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_speed_ratio` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_cast_time` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_total_time` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_delay_offset` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `attack_range` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `aram_damage_taken` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `aram_damage_dealt` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `aram_healing` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `aram_shielding` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `urf_damage_taken` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `urf_damage_dealt` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `urf_healing` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`
* `urf_shielding` -> `pyot.models.lol.merakichampion.MerakiChampionStatDetailData`

### *class* `MerakiChampionStatDetailData`

Type: `PyotStatic`

Attributes:

* `flat` -> `float`
* `percent` -> `float`
* `per_level` -> `float`
* `percent_per_level` -> `float`


# Merakiitem

Module: `pyot.models.lol.merakiitem`

### *class* `MerakiItem`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`

Endpoints:

* `meraki_item_by_id`: `['id']`

Attributes:

* `name` -> `str`
* `id` -> `int`
* `tier` -> `int`
* `rank` -> `List[str]`
* `builds_from_ids` -> `List[int]`
* `builds_into_ids` -> `List[int]`
* `no_effects` -> `bool`
* `removed` -> `bool`
* `required_champion_key` -> `str`
* `required_ally` -> `str`
* `icon` -> `str`
* `simple_description` -> `str`
* `icon_overlay` -> `bool`
* `special_recipe_id` -> `int`
* `nicknames` -> `List[str]`
* `passives` -> `List[pyot.models.lol.merakiitem.MerakiItemPassiveData]`
* `active` -> `List[pyot.models.lol.merakiitem.MerakiItemActiveData]`
* `stats` -> `pyot.models.lol.merakiitem.MerakiItemStatData`
* `shop` -> `pyot.models.lol.merakiitem.MerakiItemShopData`

Properties:

* *property* `builds_from` -> `List[ForwardRef(MerakiItem)]`
* *property* `builds_into` -> `List[ForwardRef(MerakiItem)]`
* *property* `item` -> `Item`
* *property* `required_champion` -> `MerakiChampion`
* *property* `special_recipe` -> `MerakiItem`

### *class* `MerakiItemActiveData`

Type: `PyotStatic`

Attributes:

* `unique` -> `bool`
* `name` -> `str`
* `effects` -> `str`
* `range` -> `int`
* `cooldown` -> `int`

### *class* `MerakiItemPassiveData`

Type: `PyotStatic`

Attributes:

* `unique` -> `bool`
* `name` -> `str`
* `effects` -> `str`
* `range` -> `int`
* `stats` -> `pyot.models.lol.merakiitem.MerakiItemStatData`

### *class* `MerakiItemShopData`

Type: `PyotStatic`

Attributes:

* `prices` -> `pyot.models.lol.merakiitem.MerakiItemShopPriceData`
* `purchasable` -> `bool`
* `tags` -> `List[str]`

### *class* `MerakiItemShopPriceData`

Type: `PyotStatic`

Attributes:

* `total` -> `int`
* `combined` -> `int`
* `sell` -> `int`

### *class* `MerakiItemStatData`

Type: `PyotStatic`

Attributes:

* `ability_power` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `armor` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `armor_penetration` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `attack_damage` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `attack_speed` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `cooldown_reduction` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `critical_strike_chance` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `gold_per_10` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `heal_and_shield_power` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `health` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `health_regen` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `lethality` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `lifesteal` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `tenacity` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `magic_penetration` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `magic_resistance` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `mana` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `mana_regen` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `movespeed` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `ability_haste` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`
* `omnivamp` -> `pyot.models.lol.merakiitem.MerakiItemStatDetailData`

### *class* `MerakiItemStatDetailData`

Type: `PyotStatic`

Attributes:

* `flat` -> `float`
* `percent` -> `float`
* `per_level` -> `float`
* `percent_per_level` -> `float`
* `percent_base` -> `float`
* `percent_bonus` -> `float`


# Profileicon

Module: `pyot.models.lol.profileicon`

### *class* `ProfileIcon`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_profile_icon_full`: `['version', 'locale', '?id']`

Attributes:

* `id` -> `int`
* `icon_path` -> `str`

### *class* `ProfileIcons`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lol.profileicon.ProfileIcon]`
* `__len__` -> `int`

Endpoints:

* `cdragon_profile_icon_full`: `['version', 'locale']`

Attributes:

* `icons` -> `List[pyot.models.lol.profileicon.ProfileIcon]`


# Rune

Module: `pyot.models.lol.rune`

### *class* `Rune`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_rune_full`: `['version', 'locale', '?id']`

Attributes:

* `id` -> `int`
* `name` -> `str`
* `major_patch` -> `str`
* `description` -> `str`
* `tooltip` -> `str`
* `long_description` -> `str`
* `icon_path` -> `str`
* `end_of_game_stat_descs` -> `List[str]`

### *class* `Runes`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lol.rune.Rune]`
* `__len__` -> `int`

Endpoints:

* `cdragon_rune_full`: `['version', 'locale']`

Attributes:

* `runes` -> `List[pyot.models.lol.rune.Rune]`


# Spectator

Module: `pyot.models.lol.spectator`

### *class* `CurrentGame`

Type: `PyotCore`

Extends:

* `pyot.models.lol.spectator.FeaturedGameData`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `spectator_v4_current_game`: `['summoner_id']`

Attributes:

* `id` -> `int`
* `type` -> `str`
* `mode` -> `str`
* `start_time_millis` -> `int`
* `length_secs` -> `int`
* `map_id` -> `int`
* `platform` -> `str`
* `queue_id` -> `int`
* `observers_key` -> `str`
* `teams` -> `List[pyot.models.lol.spectator.CurrentGameTeamData]`
* `summoner_id` -> `str`

Properties:

* *property* `banned_champions` -> `List[pyot.models.lol.spectator.CurrentGameBansData]`
* *property* `blue_team` -> `pyot.models.lol.spectator.CurrentGameTeamData`
* *property* `participants` -> `List[pyot.models.lol.spectator.CurrentGameParticipantData]`
* *property* `red_team` -> `pyot.models.lol.spectator.CurrentGameTeamData`
* *property* `summoner` -> `Summoner`

### *class* `FeaturedGames`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.lol.spectator.FeaturedGameData]`
* `__len__` -> `int`

Endpoints:

* `spectator_v4_featured_games`: `[]`

Attributes:

* `games` -> `List[pyot.models.lol.spectator.FeaturedGameData]`
* `refresh_interval_secs` -> `int`

Properties:

* *property* `refresh_interval` -> `datetime.timedelta`

### *class* `CurrentGameBansData`

Type: `PyotStatic`

Attributes:

* `pick_turn` -> `int`
* `champion_id` -> `int`
* `team_id` -> `int`

Properties:

* *property* `champion` -> `Champion`
* *property* `meraki_champion` -> `MerakiChampion`

### *class* `CurrentGameParticipantCustomizationData`

Type: `PyotStatic`

Attributes:

* `category` -> `str`
* `content` -> `str`

### *class* `CurrentGameParticipantData`

Type: `PyotStatic`

Attributes:

* `team_id` -> `int`
* `champion_id` -> `int`
* `profile_icon_id` -> `int`
* `is_bot` -> `bool`
* `summoner_name` -> `str`
* `summoner_id` -> `str`
* `spell_ids` -> `List[int]`
* `rune_ids` -> `List[int]`
* `rune_main_style` -> `int`
* `rune_sub_style` -> `int`
* `game_customization_objects` -> `List[pyot.models.lol.spectator.CurrentGameParticipantCustomizationData]`
* `position` -> `str`

Properties:

* *property* `champion` -> `Champion`
* *property* `meraki_champion` -> `MerakiChampion`
* *property* `profile_icon` -> `ProfileIcon`
* *property* `runes` -> `List[ForwardRef(Rune)]`
* *property* `spells` -> `List[ForwardRef(Spell)]`
* *property* `summoner` -> `Summoner`

### *class* `CurrentGameTeamData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `bans` -> `List[pyot.models.lol.spectator.CurrentGameBansData]`
* `participants` -> `List[pyot.models.lol.spectator.CurrentGameParticipantData]`

### *class* `FeaturedGameData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `type` -> `str`
* `mode` -> `str`
* `start_time_millis` -> `int`
* `length_secs` -> `int`
* `map_id` -> `int`
* `platform` -> `str`
* `queue_id` -> `int`
* `observers_key` -> `str`
* `teams` -> `List[pyot.models.lol.spectator.FeaturedGameTeamData]`

Properties:

* *property* `banned_champions` -> `List[pyot.models.lol.spectator.CurrentGameBansData]`
* *property* `blue_team` -> `pyot.models.lol.spectator.FeaturedGameTeamData`
* *property* `length` -> `datetime.timedelta`
* *property* `participants` -> `List[pyot.models.lol.spectator.FeaturedGameParticipantData]`
* *property* `red_team` -> `pyot.models.lol.spectator.FeaturedGameTeamData`
* *property* `start_time` -> `datetime.datetime`

### *class* `FeaturedGameParticipantData`

Type: `PyotStatic`

Attributes:

* `team_id` -> `int`
* `champion_id` -> `int`
* `profile_icon_id` -> `int`
* `is_bot` -> `bool`
* `summoner_name` -> `str`
* `spell_ids` -> `List[int]`
* `position` -> `str`

Properties:

* *property* `champion` -> `Champion`
* *property* `meraki_champion` -> `MerakiChampion`
* *property* `profile_icon` -> `ProfileIcon`
* *property* `spells` -> `List[ForwardRef(Spell)]`
* *property* `summoner` -> `Summoner`

### *class* `FeaturedGameTeamData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `bans` -> `List[pyot.models.lol.spectator.CurrentGameBansData]`
* `participants` -> `List[pyot.models.lol.spectator.FeaturedGameParticipantData]`


# Spell

Module: `pyot.models.lol.spell`

### *class* `Spell`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_spells_full`: `['version', 'locale', '?id']`

Attributes:

* `id` -> `int`
* `name` -> `str`
* `description` -> `str`
* `summoner_level` -> `int`
* `cooldown` -> `int`
* `modes` -> `List[str]`
* `icon_path` -> `str`

### *class* `Spells`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.lol.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lol.spell.Spell]`
* `__len__` -> `int`

Endpoints:

* `cdragon_spells_full`: `['version', 'locale']`

Attributes:

* `spells` -> `List[pyot.models.lol.spell.Spell]`


# Status

Module: `pyot.models.lol.status`

### *class* `Status`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `status_v4_platform_data`: `[]`

Attributes:

* `id` -> `str`
* `name` -> `str`
* `locales` -> `List[str]`
* `maintenances` -> `List[pyot.models.lol.status.StatusDetailData]`
* `incidents` -> `List[pyot.models.lol.status.StatusDetailData]`

### *class* `StatusContentData`

Type: `PyotStatic`

Attributes:

* `locale` -> `str`
* `content` -> `str`

### *class* `StatusDetailData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `maintenance_status` -> `str`
* `incident_severity` -> `str`
* `titles` -> `List[pyot.models.lol.status.StatusContentData]`
* `updates` -> `List[pyot.models.lol.status.StatusUpdateData]`
* `created_at_strftime` -> `str`
* `archive_at_strftime` -> `str`
* `updated_at_strftime` -> `str`
* `platforms` -> `List[str]`

Properties:

* *property* `archive_at` -> `datetime.datetime`
* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`

### *class* `StatusUpdateData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `author` -> `str`
* `publish` -> `bool`
* `publish_locations` -> `List[str]`
* `translations` -> `List[pyot.models.lol.status.StatusContentData]`
* `created_at_strftime` -> `str`
* `updated_at_strftime` -> `str`

Properties:

* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`


# Summoner

Module: `pyot.models.lol.summoner`

### *class* `Summoner`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `account_id`: `str = empty`
  * `name`: `str = empty`
  * `puuid`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `summoner_v4_by_puuid`: `['puuid']`
* `summoner_v4_by_id`: `['id']`
* `summoner_v4_by_account_id`: `['account_id']`
* `summoner_v4_by_name`: `['name']`

Attributes:

* `name` -> `str`
* `id` -> `str`
* `account_id` -> `str`
* `level` -> `int`
* `puuid` -> `str`
* `profile_icon_id` -> `int`
* `revision_date_millis` -> `int`

Properties:

* *property* `account` -> `Account`
* *property* `champion_masteries` -> `ChampionMasteries`
* *property* `clash_players` -> `ClashPlayers`
* *property* `current_game` -> `CurrentGame`
* *property* `league_entries` -> `SummonerLeague`
* *property* `match_history` -> `MatchHistory`
* *property* `profile_icon` -> `ProfileIcon`
* *property* `revision_date` -> `datetime.datetime`
* *property* `third_party_code` -> `ThirdPartyCode`


# Thirdpartycode

Module: `pyot.models.lol.thirdpartycode`

### *class* `ThirdPartyCode`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.lol.DEFAULT_PLATFORM`

Endpoints:

* `third_party_code_v4_code`: `['summoner_id']`

Attributes:

* `code` -> `str`
* `summoner_id` -> `str`

Properties:

* *property* `summoner` -> `Summoner`


# Tournament

Module: `pyot.models.lol.tournament`

### *class* `Tournament`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `region`: `str = empty`

Endpoints:

* `tournament_v4_tournaments`: `[]`

Body Params:

* `name`: `str`
* `provider_id`: `int`

Attributes:

* `id` -> `int`
* `region` -> `str`

### *class* `TournamentCode`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `code`: `str = empty`
  * `region`: `str = empty`

Endpoints:

* `tournament_v4_codes_by_code`: `['code']`

Body Params:

* `map_type`: `str`
* `pick_type`: `str`
* `spectator_type`: `str`
* `allowed_summoner_ids`: `List[str] = empty`

Attributes:

* `code` -> `str`
* `spectators` -> `str`
* `lobby_name` -> `str`
* `meta_data` -> `str`
* `password` -> `str`
* `team_size` -> `int`
* `provider_id` -> `int`
* `pick_type` -> `str`
* `tournament_id` -> `int`
* `id` -> `int`
* `map` -> `str`
* `hosted_region` -> `str`
* `summoner_ids` -> `List[str]`

Properties:

* *property* `summoners` -> `Summoner`

### *class* `TournamentCodes`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `region`: `str = empty`

Endpoints:

* `tournament_v4_codes`: `[]`

Query Params:

* `tournament_id`: `int`
* `count`: `int = empty`

Body Params:

* `map_type`: `str`
* `pick_type`: `str`
* `team_size`: `int`
* `spectator_type`: `str`
* `allowed_summoner_ids`: `List[str] = empty`
* `metadata`: `str = empty`

Attributes:

* `codes` -> `List[str]`
* `region` -> `str`

Properties:

* *property* `tournament_codes` -> `List[pyot.models.lol.tournament.TournamentCode]`

### *class* `TournamentLobbyEvents`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `code`: `int = empty`
  * `region`: `str = empty`
* `__iter__` -> `Iterator[pyot.models.lol.tournament.TournamentLobbyEventData]`
* `__len__` -> `int`

Endpoints:

* `tournament_v4_lobby_events`: `['code']`

Attributes:

* `events` -> `List[pyot.models.lol.tournament.TournamentLobbyEventData]`
* `region` -> `str`

### *class* `TournamentProvider`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `region`: `str = empty`

Endpoints:

* `tournament_v4_providers`: `[]`

Body Params:

* `region`: `str`
* `url`: `str`

Attributes:

* `id` -> `int`
* `region` -> `str`

### *class* `TournamentStub`

Type: `PyotCore`

Extends:

* `pyot.models.lol.tournament.Tournament`

Definitions:

Endpoints:

* `tournament_stub_v4_tournaments`: `[]`

Body Params:

* `name`: `str`
* `provider_id`: `int`

Attributes:

* `id` -> `int`
* `region` -> `str`

### *class* `TournamentStubCodes`

Type: `PyotCore`

Extends:

* `pyot.models.lol.tournament.TournamentCodes`

Definitions:

Endpoints:

* `tournament_stub_v4_codes`: `[]`

Query Params:

* `tournament_id`: `int`
* `count`: `int = empty`

Body Params:

* `map_type`: `str`
* `pick_type`: `str`
* `team_size`: `int`
* `spectator_type`: `str`
* `allowed_summoner_ids`: `List[str] = empty`
* `metadata`: `str = empty`

Attributes:

* `codes` -> `List[str]`
* `region` -> `str`

### *class* `TournamentStubLobbyEvents`

Type: `PyotCore`

Extends:

* `pyot.models.lol.tournament.TournamentLobbyEvents`

Definitions:

Endpoints:

* `tournament_stub_v4_lobby_events`: `['code']`

Attributes:

* `events` -> `List[pyot.models.lol.tournament.TournamentLobbyEventData]`
* `region` -> `str`

### *class* `TournamentStubProvider`

Type: `PyotCore`

Extends:

* `pyot.models.lol.tournament.TournamentProvider`

Definitions:

Endpoints:

* `tournament_stub_v4_providers`: `[]`

Body Params:

* `region`: `str`
* `url`: `str`

Attributes:

* `id` -> `int`
* `region` -> `str`

### *class* `TournamentLobbyEventData`

Type: `PyotStatic`

Attributes:

* `summoner_id` -> `str`
* `event_type` -> `str`
* `timestamp` -> `str`


# Legends of Runeterra

## Routing Regions

* `americas`
* `apac`
* `asia`
* `esports`
* `europe`
* `sea`


# Card

Module: `pyot.models.lor.card`

### *class* `Card`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `code`: `str = empty`
  * `version`: `str = models.lor.DEFAULT_VERSION`
  * `locale`: `str = models.lor.DEFAULT_LOCALE`
* `__str__` -> `str`

Endpoints:

* `ddragon_lor_set_data`: `['set', '?code', 'version', 'locale']`

Attributes:

* `associated_card_codes` -> `List[str]`
* `associated_card_refs` -> `List[str]`
* `assets` -> `List[pyot.models.lor.card.CardAssetData]`
* `region` -> `str`
* `region_ref` -> `str`
* `regions` -> `List[str]`
* `region_refs` -> `List[str]`
* `attack` -> `int`
* `cost` -> `int`
* `health` -> `int`
* `description` -> `str`
* `description_raw` -> `str`
* `levelup_description` -> `str`
* `levelup_description_raw` -> `str`
* `flavor_text` -> `str`
* `artist_name` -> `str`
* `name` -> `str`
* `code` -> `str`
* `keywords` -> `List[str]`
* `keyword_refs` -> `List[str]`
* `spell_speed` -> `str`
* `spell_speed_ref` -> `str`
* `rarity` -> `str`
* `rarity_ref` -> `str`
* `subtype` -> `str`
* `subtypes` -> `List[str]`
* `supertype` -> `str`
* `type` -> `str`
* `collectible` -> `bool`
* `set` -> `int`
* `faction` -> `str`
* `number` -> `int`
* `subcode` -> `str`

Properties:

* *property* `associated_cards` -> `List[ForwardRef(Card)]`

### *class* `Cards`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `set`: `int = empty`
  * `version`: `str = models.lor.DEFAULT_VERSION`
  * `locale`: `str = models.lor.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.lor.card.Card]`
* `__len__` -> `int`

Endpoints:

* `ddragon_lor_set_data`: `['set', 'version', 'locale']`

Attributes:

* `cards` -> `List[pyot.models.lor.card.Card]`

### *class* `CardAssetData`

Type: `PyotStatic`

Attributes:

* `game_absolute_path` -> `str`
* `full_absolute_path` -> `str`

### *class* `Batch`

Type: `PyotUtils`

Methods:

* *method* `add` -> `None`

  * `amount`: `int = 1`

  > Add a copy to the batch, `amount` may be passed to add more than 1 copy.
* *method* `remove` -> `None`

  * `amount`: `int = 1`

  > Remove a copy from the batch, `amount` may be passed to remove more than 1 copy.

Attributes:

* `code` -> `str`
* `count` -> `int`
* `faction` -> `str`
* `set` -> `int`
* `number` -> `int`

Properties:

* *property* `card` -> `pyot.models.lor.card.Card`

### *class* `Deck`

Type: `PyotUtils`

Methods:

* *method* `append` -> `None`

  * `batch`: `Union[pyot.models.lor.card.Batch, str]`

  > Appends a Batch object or CardCodeAndCount string to the Deck.
* *method* `decode` -> `None`

  > Decode the string in `self.code`, rebuild the batches and return self.
* *method* `encode` -> `str`

  > Encode the content in `self.batches`, set the code and return it.
* *method* `pop` -> `pyot.models.lor.card.Batch`

  * `ind`: `int = -1`

  > Remove and return a Batch object by index.
* *method* `pull` -> `None`

  * `card_code`: `str`

  > Remove and return a Batch object by code.

Attributes:

* `batches` -> `List[pyot.models.lor.card.Batch]`
* `code` -> `str`


# Match

Module: `pyot.models.lor.match`

### *class* `Match`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `region`: `str = models.lor.DEFAULT_REGION`

Endpoints:

* `match_v1_match`: `['id']`

Attributes:

* `id` -> `str`
* `metadata` -> `pyot.models.lor.match.MatchMetaData`
* `info` -> `pyot.models.lor.match.MatchInfoData`

### *class* `MatchHistory`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `region`: `str = models.lor.DEFAULT_REGION`
* `__iter__` -> `Iterator[pyot.models.lor.match.Match]`
* `__len__` -> `int`

Endpoints:

* `match_v1_matchlist`: `['puuid']`

Attributes:

* `ids` -> `List[str]`
* `puuid` -> `str`

Properties:

* *property* `account` -> `Account`
* *property* `matches` -> `List[pyot.models.lor.match.Match]`

### *class* `MatchInfoData`

Type: `PyotStatic`

Attributes:

* `mode` -> `str`
* `type` -> `str`
* `start_time_strftime` -> `str`
* `version` -> `str`
* `players` -> `List[pyot.models.lor.match.MatchPlayerData]`
* `total_turn_count` -> `int`

Properties:

* *property* `start_time` -> `datetime.datetime`

### *class* `MatchMetaData`

Type: `PyotStatic`

Attributes:

* `data_version` -> `str`
* `match_id` -> `str`
* `participant_puuids` -> `List[str]`

Properties:

* *property* `participants` -> `List[ForwardRef(Account)]`

### *class* `MatchPlayerData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `deck_id` -> `str`
* `deck_code` -> `str`
* `factions` -> `List[str]`
* `game_outcome` -> `str`
* `order_of_play` -> `int`
* `win` -> `bool`

Properties:

* *property* `account` -> `Account`
* *property* `deck` -> `Deck`


# Ranked

Module: `pyot.models.lor.ranked`

### *class* `Leaderboard`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `region`: `str = models.lor.DEFAULT_REGION`
* `__iter__` -> `Iterator[pyot.models.lor.ranked.LeaderboardPlayerData]`
* `__len__` -> `int`

Endpoints:

* `ranked_v1_leaderboards`: `[]`

Attributes:

* `players` -> `List[pyot.models.lor.ranked.LeaderboardPlayerData]`

### *class* `LeaderboardPlayerData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `rank` -> `int`
* `lp` -> `float`


# Status

Module: `pyot.models.lor.status`

### *class* `Status`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `region`: `str = models.lor.DEFAULT_REGION`

Endpoints:

* `status_v1_platform_data`: `[]`

Attributes:

* `id` -> `str`
* `name` -> `str`
* `locales` -> `List[str]`
* `maintenances` -> `List[pyot.models.lor.status.StatusDetailData]`
* `incidents` -> `List[pyot.models.lor.status.StatusDetailData]`

### *class* `StatusContentData`

Type: `PyotStatic`

Attributes:

* `locale` -> `str`
* `content` -> `str`

### *class* `StatusDetailData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `maintenance_status` -> `str`
* `incident_severity` -> `str`
* `created_at_strftime` -> `str`
* `updated_at_strftime` -> `str`
* `archive_at_strftime` -> `str`
* `titles` -> `List[pyot.models.lor.status.StatusContentData]`
* `updates` -> `List[pyot.models.lor.status.StatusUpdateData]`
* `platforms` -> `List[str]`

Properties:

* *property* `archive_at` -> `datetime.datetime`
* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`

### *class* `StatusUpdateData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `author` -> `str`
* `publish` -> `bool`
* `publish_locations` -> `List[str]`
* `created_at_strftime` -> `str`
* `updated_at_strftime` -> `str`
* `translations` -> `List[pyot.models.lor.status.StatusContentData]`

Properties:

* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`


# Riot Services

## Routing Regions

* `americas`
* `asia`
* `esports`
* `europe`


# Account

Module: `pyot.models.riot.account`

### *class* `Account`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `game_name`: `str = empty`
  * `tag_line`: `str = empty`
  * `region`: `str = models.riot.DEFAULT_REGION`

Endpoints:

* `account_v1_by_puuid`: `['puuid']`
* `account_v1_by_riot_id`: `['game_name', 'tag_line']`

Methods:

* *method* `active_shard` -> `None`
  * `game`: `str`

Attributes:

* `puuid` -> `str`
* `game_name` -> `str`
* `tag_line` -> `str`

### *class* `ActiveShard`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `game`: `str = empty`
  * `region`: `str = models.riot.DEFAULT_REGION`

Endpoints:

* `account_v1_active_shard`: `['puuid', 'game']`

Attributes:

* `puuid` -> `str`
* `game` -> `str`
* `active_shard` -> `str`

Properties:

* *property* `account` -> `None`


# Teamfight Tactics

## Routing Regions

* `americas`
* `asia`
* `esports`
* `europe`
* `sea`

## Routing Platforms

* `br1`
* `eun1`
* `euw1`
* `jp1`
* `kr`
* `la1`
* `la2`
* `na1`
* `oc1`
* `ru`
* `tr1`
* `ph2`
* `sg2`
* `th2`
* `tw2`
* `vn2`


# Champion

Module: `pyot.models.tft.champion`

### *class* `Champion`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `key`: `str = empty`
  * `set`: `int = empty`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_tft_full`: `['?key', '?set', 'version', 'locale']`

Methods:

* *method* `find_set` -> `None`

Attributes:

* `set` -> `int`
* `key` -> `str`
* `name` -> `str`
* `cost` -> `int`
* `stats` -> `pyot.models.tft.champion.ChampionStatData`
* `trait_keys` -> `List[str]`
* `ability` -> `pyot.models.tft.champion.ChampionAbilityData`
* `lol_id` -> `int`
* `icon_path` -> `str`

Properties:

* *property* `traits` -> `List[ForwardRef(Trait)]`

### *class* `Champions`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `set`: `int = -1`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.tft.champion.Champion]`
* `__len__` -> `int`

Endpoints:

* `cdragon_tft_full`: `['?set', 'version', 'locale']`

Attributes:

* `set` -> `int`
* `champions` -> `List[pyot.models.tft.champion.Champion]`

### *class* `ChampionAbilityData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `description` -> `str`
* `icon_path` -> `str`
* `variables` -> `List[pyot.models.tft.champion.ChampionAbilityVariableData]`

### *class* `ChampionAbilityVariableData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `value` -> `List[float]`

### *class* `ChampionStatData`

Type: `PyotStatic`

Attributes:

* `armor` -> `float`
* `attack_speed` -> `float`
* `crit_chance` -> `float`
* `crit_multiplier` -> `float`
* `damage` -> `float`
* `hp` -> `float`
* `initial_mana` -> `float`
* `magic_resist` -> `float`
* `mana` -> `float`
* `range` -> `float`


# Item

Module: `pyot.models.tft.item`

### *class* `Item`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_tft_full`: `['?id', 'version', 'locale']`

Attributes:

* `description` -> `str`
* `effects` -> `Dict[str, Union[float, str]]`
* `from_ids` -> `List[int]`
* `icon_path` -> `str`
* `id` -> `int`
* `name` -> `str`
* `unique` -> `bool`

Properties:

* *property* `from_items` -> `List[ForwardRef(Item)]`

### *class* `Items`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.tft.item.Item]`
* `__len__` -> `int`

Endpoints:

* `cdragon_tft_full`: `['version', 'locale']`

Attributes:

* `items` -> `List[pyot.models.tft.item.Item]`


# League

Module: `pyot.models.tft.league`

### *class* `ApexLeague`

Type: `PyotCore`

Extends:

* `pyot.models.tft.league.League`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`

Endpoints:

* `league_v1_league_by_league_id`: `['id']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.tft.league.LeagueEntryData]`

Properties:

* *property* `league` -> `pyot.models.tft.league.League`

### *class* `ChallengerLeague`

Type: `PyotCore`

Extends:

* `pyot.models.tft.league.ApexLeague`
* `pyot.models.tft.league.League`

Definitions:

Endpoints:

* `league_v1_challenger_league`: `[]`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.tft.league.LeagueEntryData]`

### *class* `DivisionLeague`

Type: `PyotCore`

Extends:

* `pyot.models.tft.league.SummonerLeague`

Definitions:

* `__init__` -> `None`
  * `division`: `str = empty`
  * `tier`: `str = empty`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`

Endpoints:

* `league_v1_entries_by_division`: `['tier', 'division']`

Query Params:

* `page`: `int = empty`

Attributes:

* `summoner_id` -> `str`
* `entries` -> `List[pyot.models.tft.league.SummonerLeagueEntryData]`
* `queue` -> `str`
* `division` -> `str`
* `tier` -> `str`

Properties:

* *property* `summoner` -> `NoReturn`

### *class* `GrandmasterLeague`

Type: `PyotCore`

Extends:

* `pyot.models.tft.league.ApexLeague`
* `pyot.models.tft.league.League`

Definitions:

Endpoints:

* `league_v1_grandmaster_league`: `[]`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.tft.league.LeagueEntryData]`

### *class* `League`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`

Endpoints:

* `league_v1_league_by_league_id`: `['id']`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.tft.league.LeagueEntryData]`

### *class* `MasterLeague`

Type: `PyotCore`

Extends:

* `pyot.models.tft.league.ApexLeague`
* `pyot.models.tft.league.League`

Definitions:

Endpoints:

* `league_v1_master_league`: `[]`

Attributes:

* `tier` -> `str`
* `id` -> `str`
* `queue` -> `str`
* `name` -> `str`
* `entries` -> `List[pyot.models.tft.league.LeagueEntryData]`

### *class* `SummonerLeague`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.tft.league.SummonerLeagueEntryData]`
* `__len__` -> `int`

Endpoints:

* `league_v1_summoner_entries`: `['summoner_id']`

Attributes:

* `summoner_id` -> `str`
* `entries` -> `List[pyot.models.tft.league.SummonerLeagueEntryData]`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `LeagueEntryData`

Type: `PyotStatic`

Attributes:

* `summoner_id` -> `str`
* `summoner_name` -> `str`
* `league_points` -> `int`
* `rank` -> `str`
* `wins` -> `int`
* `losses` -> `int`
* `veteran` -> `bool`
* `inactive` -> `bool`
* `fresh_blood` -> `bool`
* `hot_streak` -> `bool`
* `mini_series` -> `pyot.models.tft.league.MiniSeriesData`

Properties:

* *property* `summoner` -> `Summoner`

### *class* `MiniSeriesData`

Type: `PyotStatic`

Attributes:

* `target` -> `int`
* `wins` -> `int`
* `losses` -> `int`
* `progress` -> `str`

### *class* `SummonerLeagueEntryData`

Type: `PyotStatic`

Extends:

* `pyot.models.tft.league.LeagueEntryData`

Attributes:

* `summoner_id` -> `str`
* `summoner_name` -> `str`
* `league_points` -> `int`
* `rank` -> `str`
* `wins` -> `int`
* `losses` -> `int`
* `veteran` -> `bool`
* `inactive` -> `bool`
* `fresh_blood` -> `bool`
* `hot_streak` -> `bool`
* `mini_series` -> `pyot.models.tft.league.MiniSeriesData`
* `league_id` -> `str`
* `queue` -> `str`
* `tier` -> `str`

Properties:

* *property* `league` -> `League`


# Match

Module: `pyot.models.tft.match`

### *class* `Match`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `region`: `str = models.tft.DEFAULT_REGION`

Endpoints:

* `match_v1_match`: `['id']`

Attributes:

* `id` -> `str`
* `info` -> `pyot.models.tft.match.MatchInfoData`
* `metadata` -> `pyot.models.tft.match.MatchMetadataData`

### *class* `MatchHistory`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `region`: `str = models.tft.DEFAULT_REGION`
* `__iter__` -> `Iterator[pyot.models.tft.match.Match]`
* `__len__` -> `int`

Endpoints:

* `match_v1_matchlist`: `['puuid']`

Query Params:

* `count`: `int = 20`

Attributes:

* `ids` -> `List[str]`
* `puuid` -> `str`

Properties:

* *property* `matches` -> `List[pyot.models.tft.match.Match]`
* *property* `summoner` -> `Summoner`

### *class* `MatchInfoCompanionData`

Type: `PyotStatic`

Attributes:

* `content_id` -> `str`
* `skin_id` -> `int`
* `species` -> `str`

### *class* `MatchInfoData`

Type: `PyotStatic`

Attributes:

* `datetime_millis` -> `int`
* `length_secs` -> `float`
* `variation` -> `str`
* `version` -> `str`
* `participants` -> `List[pyot.models.tft.match.MatchInfoParticipantData]`
* `queue_id` -> `int`
* `tft_mode` -> `str`
* `tft_set_number` -> `int`

Properties:

* *property* `datetime` -> `datetime.datetime`
* *property* `length` -> `datetime.timedelta`

### *class* `MatchInfoParticipantData`

Type: `PyotStatic`

Attributes:

* `companion` -> `pyot.models.tft.match.MatchInfoCompanionData`
* `gold_left` -> `int`
* `last_round` -> `int`
* `level` -> `int`
* `placement` -> `int`
* `players_eliminated` -> `int`
* `puuid` -> `str`
* `time_eliminated_secs` -> `float`
* `total_damage_to_players` -> `int`
* `traits` -> `List[pyot.models.tft.match.MatchInfoTraitData]`
* `units` -> `List[pyot.models.tft.match.MatchInfoUnitData]`

Properties:

* *property* `summoner` -> `Summoner`
* *property* `time_eliminated` -> `datetime.timedelta`

### *class* `MatchInfoTraitData`

Type: `PyotStatic`

Attributes:

* `name` -> `str`
* `num_units` -> `int`
* `style` -> `int`
* `tier_current` -> `int`
* `tier_total` -> `int`

Properties:

* *property* `trait` -> `Trait`

### *class* `MatchInfoUnitData`

Type: `PyotStatic`

Attributes:

* `item_ids` -> `List[int]`
* `champion_key` -> `str`
* `chosen` -> `str`
* `name` -> `str`
* `rarity` -> `int`
* `tier` -> `int`

Properties:

* *property* `champion` -> `Champion`
* *property* `items` -> `List[ForwardRef(Item)]`

### *class* `MatchMetadataData`

Type: `PyotStatic`

Attributes:

* `id` -> `str`
* `data_version` -> `str`
* `participant_puuids` -> `List[str]`

Properties:

* *property* `participants` -> `List[ForwardRef(Summoner)]`


# Profileicon

Module: `pyot.models.tft.profileicon`

### *class* `ProfileIcon`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `int = empty`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_profile_icon_full`: `['?id', 'version', 'locale']`

Attributes:

* `id` -> `int`
* `icon_path` -> `str`

### *class* `ProfileIcons`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.tft.profileicon.ProfileIcon]`
* `__len__` -> `int`

Endpoints:

* `cdragon_profile_icon_full`: `['version', 'locale']`

Attributes:

* `icons` -> `List[pyot.models.tft.profileicon.ProfileIcon]`


# Summoner

Module: `pyot.models.tft.summoner`

### *class* `Summoner`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `account_id`: `str = empty`
  * `name`: `str = empty`
  * `puuid`: `str = empty`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`

Endpoints:

* `summoner_v1_by_id`: `['id']`
* `summoner_v1_by_account_id`: `['account_id']`
* `summoner_v1_by_puuid`: `['puuid']`
* `summoner_v1_by_name`: `['name']`

Attributes:

* `name` -> `str`
* `id` -> `str`
* `account_id` -> `str`
* `level` -> `int`
* `puuid` -> `str`
* `profile_icon_id` -> `int`
* `revision_date_millis` -> `int`

Properties:

* *property* `account` -> `Account`
* *property* `league_entries` -> `SummonerLeague`
* *property* `match_history` -> `MatchHistory`
* *property* `profile_icon` -> `ProfileIcon`
* *property* `revision_date` -> `datetime.datetime`
* *property* `third_party_code` -> `ThirdPartyCode`


# Thirdpartycode

Module: `pyot.models.tft.thirdpartycode`

### *class* `ThirdPartyCode`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `summoner_id`: `str = empty`
  * `platform`: `str = models.tft.DEFAULT_PLATFORM`

Endpoints:

* `third_party_code_v4_code`: `['summoner_id']`

Attributes:

* `code` -> `str`
* `summoner_id` -> `str`

Properties:

* *property* `summoner` -> `Summoner`


# Trait

Module: `pyot.models.tft.trait`

### *class* `Trait`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `key`: `str = empty`
  * `set`: `int = empty`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`

Endpoints:

* `cdragon_tft_full`: `['?set', '?key', 'version', 'locale']`

Methods:

* *method* `find_set` -> `None`

Attributes:

* `set` -> `int`
* `key` -> `str`
* `name` -> `str`
* `effects` -> `List[pyot.models.tft.trait.TraitEffectData]`
* `icon_path` -> `str`
* `description` -> `str`

### *class* `Traits`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `set`: `int = -1`
  * `version`: `str = models.tft.DEFAULT_VERSION`
  * `locale`: `str = models.lol.DEFAULT_LOCALE`
* `__iter__` -> `Iterator[pyot.models.tft.trait.Trait]`
* `__len__` -> `int`

Endpoints:

* `cdragon_tft_full`: `['?set', 'version', 'locale']`

Attributes:

* `set` -> `int`
* `traits` -> `List[pyot.models.tft.trait.Trait]`

### *class* `TraitEffectData`

Type: `PyotStatic`

Attributes:

* `max_units` -> `int`
* `min_units` -> `int`
* `style` -> `int`
* `variables` -> `Dict[str, Union[float, str]]`


# Valorant

## Routing Regions

* `americas`
* `asia`
* `esports`
* `europe`

## Routing Platforms

* `ap`
* `br`
* `esports`
* `eu`
* `kr`
* `latam`
* `na`


# Content

Module: `pyot.models.val.content`

### *class* `Content`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`

Endpoints:

* `content_v1_contents`: `[]`

Query Params:

* `locale`: `str = empty`

Attributes:

* `version` -> `str`
* `characters` -> `List[pyot.models.val.content.ContentItemData]`
* `maps` -> `List[pyot.models.val.content.ContentItemData]`
* `chromas` -> `List[pyot.models.val.content.ContentItemData]`
* `skins` -> `List[pyot.models.val.content.ContentItemData]`
* `skin_levels` -> `List[pyot.models.val.content.ContentItemData]`
* `equips` -> `List[pyot.models.val.content.ContentItemData]`
* `game_modes` -> `List[pyot.models.val.content.ContentItemData]`
* `sprays` -> `List[pyot.models.val.content.ContentItemData]`
* `spray_levels` -> `List[pyot.models.val.content.ContentItemData]`
* `charms` -> `List[pyot.models.val.content.ContentItemData]`
* `charm_levels` -> `List[pyot.models.val.content.ContentItemData]`
* `player_cards` -> `List[pyot.models.val.content.ContentItemData]`
* `player_titles` -> `List[pyot.models.val.content.ContentItemData]`
* `ceremonies` -> `List[pyot.models.val.content.ContentItemData]`
* `acts` -> `List[pyot.models.val.content.ContentActData]`

### *class* `ContentActData`

Type: `PyotStatic`

Attributes:

* `id` -> `str`
* `name` -> `str`
* `type` -> `str`
* `parent_id` -> `str`
* `localized_names` -> `pyot.models.val.content.ContentLocalizedNamesData`
* `is_active` -> `bool`

Properties:

* *property* `leaderboard` -> `Leaderboard`

### *class* `ContentItemData`

Type: `PyotStatic`

Attributes:

* `id` -> `str`
* `name` -> `str`
* `asset_name` -> `str`
* `asset_path` -> `str`
* `localized_names` -> `pyot.models.val.content.ContentLocalizedNamesData`

### *class* `ContentLocalizedNamesData`

Type: `PyotStatic`

Attributes:

* `ar_ae` -> `str`
* `de_de` -> `str`
* `en_gb` -> `str`
* `en_us` -> `str`
* `es_es` -> `str`
* `es_mx` -> `str`
* `fr_fr` -> `str`
* `id_id` -> `str`
* `it_it` -> `str`
* `ja_jp` -> `str`
* `ko_kr` -> `str`
* `pl_pl` -> `str`
* `pt_br` -> `str`
* `ru_ru` -> `str`
* `th_th` -> `str`
* `tr_tr` -> `str`
* `vi_vn` -> `str`
* `zh_cn` -> `str`
* `zh_tw` -> `str`


# Match

Module: `pyot.models.val.match`

### *class* `Match`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `id`: `str = empty`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`

Endpoints:

* `match_v1_match`: `['id']`

Attributes:

* `id` -> `str`
* `info` -> `pyot.models.val.match.MatchInfoData`
* `players` -> `List[pyot.models.val.match.MatchPlayerData]`
* `teams` -> `List[pyot.models.val.match.MatchTeamData]`
* `coaches` -> `List[pyot.models.val.match.MatchCoachData]`
* `round_results` -> `List[pyot.models.val.match.MatchRoundResultData]`
* `start_time_millis` -> `int`
* `team_id` -> `str`
* `queue_id` -> `str`

Properties:

* *property* `start_time` -> `datetime.datetime`

### *class* `MatchHistory`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `puuid`: `str = empty`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.val.match.Match]`
* `__len__` -> `int`

Endpoints:

* `match_v1_matchlist`: `['puuid']`

Attributes:

* `puuid` -> `str`
* `history` -> `List[pyot.models.val.match.Match]`

Properties:

* *property* `account` -> `Account`

### *class* `RecentMatches`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `queue`: `str = empty`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.val.match.Match]`
* `__len__` -> `int`

Endpoints:

* `match_v1_recent`: `['queue']`

Attributes:

* `current_timestamp` -> `int`
* `match_ids` -> `List[str]`

Properties:

* *property* `current_time` -> `datetime.datetime`
* *property* `matches` -> `List[pyot.models.val.match.Match]`

### *class* `MatchCoachData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `team_id` -> `str`

### *class* `MatchInfoData`

Type: `PyotStatic`

Attributes:

* `id` -> `str`
* `map_url` -> `str`
* `start_millis` -> `int`
* `length_millis` -> `int`
* `provisioning_flow_id` -> `str`
* `is_completed` -> `bool`
* `custom_game_name` -> `str`
* `queue_id` -> `str`
* `game_mode` -> `str`
* `game_version` -> `str`
* `is_ranked` -> `bool`
* `season_id` -> `str`

Properties:

* *property* `length` -> `datetime.timedelta`
* *property* `start` -> `datetime.datetime`

### *class* `MatchLocationData`

Type: `PyotStatic`

Attributes:

* `x` -> `int`
* `y` -> `int`

### *class* `MatchPlayerAbilityCastData`

Type: `PyotStatic`

Attributes:

* `grenade_casts` -> `int`
* `ability1_casts` -> `int`
* `ability2_casts` -> `int`
* `ultimate_casts` -> `int`

### *class* `MatchPlayerAbilityData`

Type: `PyotStatic`

Attributes:

* `grenade_effects` -> `int`
* `ability1_effects` -> `int`
* `ability2_effects` -> `int`
* `ultimate_effects` -> `int`

### *class* `MatchPlayerDamageData`

Type: `PyotStatic`

Attributes:

* `receiver` -> `str`
* `damage` -> `int`
* `legshots` -> `int`
* `bodyshots` -> `int`
* `headshots` -> `int`

### *class* `MatchPlayerData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `game_name` -> `str`
* `tag_line` -> `str`
* `team_id` -> `str`
* `party_id` -> `str`
* `character_id` -> `str`
* `stats` -> `pyot.models.val.match.MatchPlayerStatData`
* `competitive_tier` -> `int`
* `player_card_id` -> `str`
* `player_title_id` -> `str`

Properties:

* *property* `account` -> `Account`

### *class* `MatchPlayerEconomyData`

Type: `PyotStatic`

Attributes:

* `loadout_value` -> `int`
* `weapon_id` -> `str`
* `armor_id` -> `str`
* `remaining` -> `int`
* `spent` -> `int`

### *class* `MatchPlayerFinishingDamageData`

Type: `PyotStatic`

Attributes:

* `damage_type` -> `str`
* `damage_item` -> `str`
* `is_secondary_fire_mode` -> `bool`

### *class* `MatchPlayerKillData`

Type: `PyotStatic`

Attributes:

* `game_time_millis` -> `int`
* `round_time_millis` -> `int`
* `killer_puuid` -> `str`
* `victim_puuid` -> `str`
* `victim_location` -> `pyot.models.val.match.MatchLocationData`
* `assistant_puuids` -> `List[str]`
* `player_locations` -> `List[pyot.models.val.match.MatchPlayerLocationData]`
* `finishing_damage` -> `pyot.models.val.match.MatchPlayerFinishingDamageData`

Properties:

* *property* `assistants` -> `List[ForwardRef(Account)]`
* *property* `game_time` -> `datetime.timedelta`
* *property* `killer` -> `Account`
* *property* `round_time` -> `datetime.timedelta`
* *property* `victim` -> `Account`

### *class* `MatchPlayerLocationData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `view_radians` -> `float`
* `location` -> `pyot.models.val.match.MatchLocationData`

### *class* `MatchPlayerRoundStatData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `kills` -> `List[pyot.models.val.match.MatchPlayerKillData]`
* `damage` -> `List[pyot.models.val.match.MatchPlayerDamageData]`
* `score` -> `int`
* `economy` -> `pyot.models.val.match.MatchPlayerEconomyData`
* `ability` -> `pyot.models.val.match.MatchPlayerAbilityData`

### *class* `MatchPlayerStatData`

Type: `PyotStatic`

Attributes:

* `score` -> `int`
* `rounds_played` -> `int`
* `kills` -> `int`
* `deaths` -> `int`
* `assists` -> `int`
* `playtime_millis` -> `int`
* `ability_casts` -> `pyot.models.val.match.MatchPlayerAbilityCastData`

Properties:

* *property* `playtime` -> `datetime.timedelta`

### *class* `MatchRoundResultData`

Type: `PyotStatic`

Attributes:

* `round_num` -> `int`
* `round_result` -> `str`
* `round_ceremony` -> `str`
* `winning_team` -> `str`
* `bomb_planter_puuid` -> `str`
* `bomb_defuser_puuid` -> `str`
* `plant_round_millis` -> `int`
* `plant_player_locations` -> `List[pyot.models.val.match.MatchPlayerLocationData]`
* `plant_location` -> `pyot.models.val.match.MatchLocationData`
* `plant_site` -> `str`
* `defuse_round_millis` -> `int`
* `defuse_player_locations` -> `List[pyot.models.val.match.MatchPlayerLocationData]`
* `defuse_location` -> `pyot.models.val.match.MatchLocationData`
* `player_stats` -> `List[pyot.models.val.match.MatchPlayerRoundStatData]`
* `round_result_code` -> `str`

Properties:

* *property* `bomb_defuser` -> `Account`
* *property* `bomb_planter` -> `Account`
* *property* `defuse_round_time` -> `datetime.timedelta`
* *property* `plant_round_time` -> `datetime.timedelta`

### *class* `MatchTeamData`

Type: `PyotStatic`

Attributes:

* `id` -> `str`
* `won` -> `bool`
* `rounds_played` -> `int`
* `rounds_won` -> `int`
* `num_points` -> `int`


# Ranked

Module: `pyot.models.val.ranked`

### *class* `Leaderboard`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `act_id`: `str = empty`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`
* `__iter__` -> `Iterator[pyot.models.val.ranked.LeaderboardPlayerData]`
* `__len__` -> `int`

Endpoints:

* `ranked_v1_leaderboards`: `['act_id']`

Query Params:

* `size`: `int = 200`
* `start_index`: `int = 0`

Attributes:

* `act_id` -> `str`
* `total_players` -> `int`
* `players` -> `List[pyot.models.val.ranked.LeaderboardPlayerData]`
* `immortal_starting_page` -> `int`
* `immortal_starting_index` -> `int`
* `top_tier_rr_threshold` -> `int`
* `tier_details` -> `Dict[str, pyot.models.val.ranked.LeaderboardTierDetailData]`
* `start_index` -> `int`
* `query_str` -> `str`
* `shard` -> `str`

### *class* `LeaderboardPlayerData`

Type: `PyotStatic`

Attributes:

* `puuid` -> `str`
* `game_name` -> `str`
* `tag_line` -> `str`
* `leaderboard_rank` -> `int`
* `ranked_rating` -> `int`
* `number_of_wins` -> `int`
* `competitive_tier` -> `int`

Properties:

* *property* `account` -> `Account`

### *class* `LeaderboardTierDetailData`

Type: `PyotStatic`

Attributes:

* `ranked_rating_threshold` -> `int`
* `starting_page` -> `int`
* `starting_index` -> `int`


# Status

Module: `pyot.models.val.status`

### *class* `Status`

Type: `PyotCore`

Definitions:

* `__init__` -> `None`
  * `platform`: `str = models.val.DEFAULT_PLATFORM`

Endpoints:

* `status_v1_platform_data`: `[]`

Attributes:

* `id` -> `str`
* `name` -> `str`
* `locales` -> `List[str]`
* `maintenances` -> `List[pyot.models.val.status.StatusDetailData]`
* `incidents` -> `List[pyot.models.val.status.StatusDetailData]`

### *class* `StatusContentData`

Type: `PyotStatic`

Attributes:

* `locale` -> `str`
* `content` -> `str`

### *class* `StatusDetailData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `maintenance_status` -> `str`
* `incident_severity` -> `str`
* `titles` -> `List[pyot.models.val.status.StatusContentData]`
* `updates` -> `List[pyot.models.val.status.StatusUpdateData]`
* `created_at_strftime` -> `str`
* `archive_at_strftime` -> `str`
* `updated_at_strftime` -> `str`
* `platforms` -> `List[str]`

Properties:

* *property* `archive_at` -> `datetime.datetime`
* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`

### *class* `StatusUpdateData`

Type: `PyotStatic`

Attributes:

* `id` -> `int`
* `author` -> `str`
* `publish` -> `bool`
* `publish_locations` -> `List[str]`
* `translations` -> `List[pyot.models.val.status.StatusContentData]`
* `created_at_strftime` -> `str`
* `updated_at_strftime` -> `str`

Properties:

* *property* `created_at` -> `datetime.datetime`
* *property* `updated_at` -> `datetime.datetime`


# Utils


# LoL


# Cdragon

Module: `pyot.utils.lol.cdragon`

### *constant* `BASE_URL`: `https://raw.communitydragon.org/`

### *function* `abs_url` -> `str`

* `link`: `str`
* `version`: `str = latest`

> Return the CDragon url for the given game asset url

### *function* `sanitize_description` -> `str`

* `string`: `str`

> Sanitize CDragon descriptions

### *function* `strip_k` -> `str`

* `string`: `str`

> Strips char k if string start with k


# Champion

Module: `pyot.utils.lol.champion`

### *class* `ChampionKeysCache`

Definitions:

* `__init__` -> `None`
* `__str__` -> `str`

### *constant* `champion_keys_cache`: `ChampionKeysCache()`

### *asyncfunction* `id_by_key` -> `int`

* `value`: `str`

> Get champion id by key

### *asyncfunction* `id_by_name` -> `int`

* `value`: `str`

> Get champion id by name

### *asyncfunction* `key_by_id` -> `str`

* `value`: `int`

> Get champion key by id

### *asyncfunction* `key_by_name` -> `str`

* `value`: `str`

> Get champion key by name

### *asyncfunction* `name_by_id` -> `str`

* `value`: `int`

> Get champion name by id

### *asyncfunction* `name_by_key` -> `str`

* `value`: `str`

> Get champion name by key


# Routing

Module: `pyot.utils.lol.routing`

### *function* `platform_to_region` -> `str`

* `platform`: `str`

> Return the region correspondent to a given platform


# LoR


# Cards

Module: `pyot.utils.lor.cards`

### *function* `batch_to_ccac` -> `None`

* `batch`: `None`

> Converts a Batch object to CardCodeAndCount object.

### *function* `ccac_to_batch` -> `None`

* `ccac`: `lor_deckcodes.models.CardCodeAndCount`

> Converts a CardCodeAndCount object to Batch object.


# Tft


# Cdragon

Module: `pyot.utils.tft.cdragon`

### *constant* `BASE_URL`: `https://raw.communitydragon.org/`

### *function* `abs_url` -> `str`

* `link`: `str`
* `version`: `str = latest`

> Return the CDragon url for the given tft asset url

### *function* `merge_set_data` -> `None`

* `data`: `Dict`
* `set`: `int`
* `collection_key`: `str`

### *function* `sanitize_champion_description` -> `str`

* `string`: `str`
* `list_of_obj`: `list`

> Sanitize CDragon tft champion descriptions

### *function* `sanitize_item_description` -> `str`

* `string`: `str`
* `obj`: `dict`

> Sanitize CDragon tft item descriptions


# Routing

Module: `pyot.utils.tft.routing`

### *function* `platform_to_region` -> `str`

* `platform`: `str`

> Return the region correspondent to a given platform


# Aiohttp

Module: `pyot.utils.aiohttp`

### *class* `SafeClientSession`

Extends:

* `aiohttp.client.ClientSession`

Methods:

* *asyncmethod* `close` -> `None`

  > Graceful close and release all resources.


# Copy

Module: `pyot.utils.copy`

### *function* `fast_copy` -> `~T`

* `obj`: `~T`

> 30x faster copy than `copy.deepcopy`, but not all objects can be fast copied (e.g. lambdas).


# Functools

Module: `pyot.utils.functools`

### *class* `async_cached_property`

> Async equivalent of `functools.cached_property`, takes an async method and converts it to a cached property that returns an awaitable with the return value.
>
> Usage:
>
> ```python
> class A:
>     @async_cached_property
>     async def b(self):
>         ...
> a = A()
> await a.b
> ```

Extends:

* `pyot.utils.functools.async_property`
* `Generic`

Definitions:

* `__init__` -> `None`
  * `func`: `Callable[..., Awaitable[~R]]`
  * `name`: `None`

Methods:

* *asyncmethod* `proxy` -> `Awaitable[~R]`
  * `instance`: `Any`

### *class* `async_generator_property`

> Modified version of `async_property`, intended for use in async generators. The return typing of the decorated property is: AsyncGenerator\[...]
>
> Usage:
>
> ```python
> class A:
>     @async_generator_property
>     async def b(self):
>         yield ...
> a = A()
> async for _ in a.b:
>     ...
> ```

Extends:

* `pyot.utils.functools.async_property`
* `Generic`

Definitions:

* `__get__` -> `Awaitable[~R]`
  * `instance`: `None`
  * `cls`: `None`
* `__init__` -> `None`
  * `func`: `Callable[..., AsyncGenerator[~IY, ~IS]]`
  * `name`: `None`
* `__set__` -> `None`
  * `obj`: `None`
  * `value`: `None`

Methods:

* *method* `proxy` -> `Awaitable[~R]`
  * `instance`: `Any`

### *class* `async_property`

> Async equivalent of `property`, takes an async method and converts it to a property that returns an awaitable with the return value.
>
> Usage:
>
> ```python
> class A:
>     @async_property
>     async def b(self):
>         ...
> a = A()
> await a.b
> ```

Extends:

* `Generic`

Definitions:

* `__get__` -> `Awaitable[~R]`
  * `instance`: `None`
  * `cls`: `None`
* `__init__` -> `None`
  * `func`: `Callable[..., Awaitable[~R]]`
  * `name`: `None`
* `__set__` -> `None`
  * `obj`: `None`
  * `value`: `None`
* `__set_name__` -> `None`
  * `owner`: `None`
  * `name`: `None`

Methods:

* *classmethod* `func` -> `Any`
  * `instance`: `Any`
* *asyncmethod* `proxy` -> `Awaitable[~R]`
  * `instance`: `Any`

### *function* `cached_property` -> `~R`

* `func`: `Callable[..., ~R]`


# Importlib

Module: `pyot.utils.importlib`

### *function* `import_variable` -> `Any`

* `path`: `str`

> Return the class given its python path


# Itertools

Module: `pyot.utils.itertools`

### *class* `FrozenGenerator`

> Generator that isolates the original list by returning copies of objects when iterated. Used for preventing memory leaks of self-filled objects at the cost of performance.

Extends:

* `Generic`

Definitions:

* `__init__` -> `None`
  * `li`: `List[~T]`
* `__iter__` -> `Iterator[~T]`

### *alias* `frozen_generator` \~ `FrozenGenerator`


# Logging

Module: `pyot.utils.logging`

### *class* `LazyLogger`

> Lazy logger which its `log()` method will do nothing if level equals 0

Extends:

* `logging.Logger`
* `logging.Filterer`

Definitions:

* `__init__` -> `None`
  * `name`: `None`

Methods:

* *method* `log` -> `None`

  * `level`: `None`

  * `msg`: `None`

  > Same as logging.Logger.log, with a new level (0) to skip logging.


# Nullsafe

Module: `pyot.utils.nullsafe`

### *class* `NullSafe`

> Documentation at: <https://github.com/iann838/nullsafe-python>

Definitions:

* `__bool__` -> `None`
* `__call__` -> `NullSafe`
  * `args`: `Any`
  * `kwds`: `Any`
* `__eq__` -> `bool`
  * `o`: `object`
* `__getattr__` -> `NullSafe`
  * `k`: `str`
* `__getitem__` -> `NullSafe`
  * `k`: `str`
* `__iter__` -> `None`
* `__repr__` -> `str`
* `__setattr__` -> `None`
  * `name`: `str`
  * `value`: `Any`
* `__str__` -> `str`

### *class* `NullSafeProxy`

Extends:

* `Generic`

Definitions:

* `__getattr__` -> `Union[Any, pyot.utils.nullsafe.NullSafe]`
  * `name`: `str`
* `__getitem__` -> `Union[Any, pyot.utils.nullsafe.NullSafe]`
  * `k`: `str`
* `__init__` -> `None`
  * `o`: `~T`
* `__repr__` -> `str`
* `__setattr__` -> `None`
  * `name`: `str`
  * `value`: `Any`
* `__str__` -> `str`

### *alias* `_` \~ `nullsafe`

### *function* `nullsafe` -> `Union[~T, pyot.utils.nullsafe.NullSafe, pyot.utils.nullsafe.NullSafeProxy[~T]]`

* `o`: `~T`

### *constant* `undefined`: `undefined`


# Safejson

Module: `pyot.utils.safejson`

### *function* `load` -> `Any`

* `fp`: `Union[_io.FileIO, _io.BytesIO]`
* `kwargs`: `None`

> Same as json.load with graceful fallback by returning the read content as is

### *function* `loads` -> `Any`

* `content`: `Union[str, bytes]`
* `kwargs`: `None`

> Same as json.loads with graceful fallback by returning the passed content as is


# Sync

Module: `pyot.utils.sync`

### *function* `async_to_sync` -> `Callable[..., ~R]`

* `func`: `Callable[..., Awaitable[~R]]`

> Wraps `asyncio.run` on an async function converting it into a blocking function. Can be used as decorator @async\_to\_sync

### *function* `sync_to_async` -> `Callable[..., Awaitable[~R]]`

* `func`: `Callable[..., ~R]`

> Wraps `asyncio.get_event_loop().run_in_executor` on a blocking function converting it into a Future. Can be used as decorator @sync\_to\_async


# Text

Module: `pyot.utils.text`

### *function* `camel_case` -> `str`

* `snake_str`: `str`

> Convert string to json camel\_case.

### *function* `snake_case` -> `str`

* `attr`: `str`
* `sep_numbers`: `bool = False`

> Convert string to python snake\_case.


# Threading

Module: `pyot.utils.threading`

### *class* `AsyncLock`

> An asynchronous threading Lock. The event loop won't be blocked when acquiring the lock.

Definitions:

* `__aenter__` -> `bool`
  * `args`: `None`
* `__aexit__` -> `None`
  * `args`: `None`
* `__init__` -> `None`

Methods:

* *asyncmethod* `acquire` -> `bool`

  > Acquire the lock without locking the loop
* *method* `release` -> `None`

  > Release the lock, this is not async because it is immediate and useful for hooks (e.g. registering `atexit`)


# Integrations


# Django

Integration with Django.

## Setup

1. Set up Django project.
2. Create a file (generally called `pyotconf.py`) under any project module.
3. Configure the models and pipelines inside this file.
4. Add `pyot` to the `INSTALLED_APPS` of the project `settings.py` file.
5. Add the file path of the configuration file to a new `settings.py` variable `PYOT_CONFS` (list or iterable).

```python
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pyot',
]
```

```python
PYOT_CONFS = ['mysite.pyotconf.py']
```

{% hint style="info" %}
The variable `PYOT_CONFS` can accept multiple files in case configurations are organized in separated files.
{% endhint %}

## Views

{% hint style="danger" %}
If wsgi is used (unsure about asgi), it is forced to run async views in threads, make sure to use resource managers for graceful handling of resources, refer to **Cores -> Resources**.
{% endhint %}

```python
from django.views import View
from asgiref.sync import async_to_sync
from pyot.core.resources import resource_manager

@resource_manager.as_decorator
async def function_based_view_using_decorator(request):
    ...

async def function_based_view_using_context_manager(request):
    async with resource_manager():
        ...

class ClassBasedDecoratedView(View):
    @resource_manager.as_decorator
    async def get(self, request):
        ...

class ClassBasedContextManagedView(View):
    async def get(request):
        async with resource_manager():
            ...
```


# FastAPI

Integration with FastAPI.

## Setup

1. Setup FastAPI project.
2. Configure pyot models and pipelines.
3. Import conf file in startup event.

```python
# Other imports ...
from pyot.conf.utils import import_confs

# FastAPI stuff ...

@app.on_event("startup")
async def startup_tasks():
    import_confs("<pyotconf_import_path>")
```

{% hint style="info" %}
Import path is the path used as if the file/module is being imported using python syntax via `import`, `__import__` or `importlib.import_module`
{% endhint %}


# Celery

Integration with Celery.

## Setup

1. Set up Celery project.
2. Configure pyot models and pipelines.

```python
# Other imports ...
from pyot.conf.utils import import_confs

# Celery settings stuff ...

import_confs("<pyotconf_import_path>")
```

{% hint style="info" %}
Import path is the path used as if the file/module is being imported using python syntax via `import`, `__import__` or `importlib.import_module`
{% endhint %}

## Tasks

Celery does not support async functions, a util wrapper is provided `async_to_sync` in `pyot.utils.sync` to convert async functions into blocking functions.

{% hint style="danger" %}
Celery runs tasks in threads or processes, make sure to use resource managers for graceful handling of resources, refer to **Cores -> Resources**.
{% endhint %}

```python
from pyot.core.resources import resource_manager
from pyot.utils.sync import async_to_sync

# ...

@app.task
@async_to_sync
@resource_manager.as_decorator
async def task_using_decorator():
    ...

# OR

@app.task
@async_to_sync
async def task_using_context_manager():
    async with resource_manager():
        ...
```


# Changelog


# 5.x.x

## 5.3.3

* Revert `ChampionKeysCache` lock change, but `asyncio.Lock` is now on delayed instantiation.
* Remove unused code from `limiters.RedisRateLimiter`

## 5.3.2

* New data fields detected and added on `lol.Match`, `val.Match`.
* Updated test player for `test_models_lor.test_match`.
* Change the Lock on `ChampionKeysCache` to `SealLock`, as the `asyncio.Lock` requires an event loop at the moment of instantiation, an issue that may be encountered by non-async "tasks explorers".

## 5.3.1

* Improved documentation engines.
* Added some missing typings.

## 5.3.0

* New `pyot.utils.functools` module.
* New utilities `async_property` and `async_cached_property` in `pyot.utils.functools`.
* New utility `sync_to_async` in `pyot.utils.sync`.
* Removed `PtrCache` from `pyot.utils.cache`, use `functools.lru_cache` instead.
* Removed `cached_property` from `pyot.utils.cache`, use `functools.cached_property` instead.
* Removed `pyot.utils.cache` module.
* Reworked `pyot.utils.lol.champion` (interfaces unchanged).
* Fixed some typings in util modules.
* Fixed a bug where `pyot.core.functional.lazy_property` is not caching returned values.

## 5.2.0

* `lol.MerakiItem` removed `meraki_` prefixed properties, non-prefixed properties will replace them. To keep having access to `lol.Item`s properties, use the `item` property instead.
* Added challenges to match-v5 participants.
* Added missing attributes typings.
* Added more unit test modules.
* Improved internal serialization.
* Renamed event loop utility `LoopSensitiveManager` to `EventLoopFactory`.
* Added `manage_threaded_resources` for cleaning resources in threaded environment.
* Deprecating `PYOT_SETTINGS` variable for django integrations in favor of `PYOT_CONFS`.
* The integration code of `pyot.__init__` has been moved into the new `integrations` module.

## 5.1.0

* Pyot new documentations.
* Some bug fixes.
* Attribute changes.

## 5.0.0

* Reworked pipeline.
* Reworked model and pipeline conf.
* No more sid, sessions are managed internally.
* Match-v5 initial support.
* Change of behavior of multiple core models.
* Reworked rate limiters to be more secure and accurate.
* Dropped syot.


# 6.x.x

## 6.0.5

Hotfix (for those who believe `oc1` platform exists):

* In 2022/6/29, a data migration happened for the `oc1` platform, moving all the data from `americas` region to `sea` region, it is now reflected in Pyot aswell (after almost 2 months because I forgot that `oc1` exists).

## 6.0.4

Improvement:

* Improved documentation syntax and type hints.

## 6.0.3

Hotfix (rare-breaking bug):

* Type error after rate limit returns non-service 429.

## 6.0.2

Hotfix (breaking bug):

* Class `Queue` from `pyot.core.queue` is not properly printing raised exceptions inside workers and causing workers to fail after an exception is raised. Now fixed.

## 6.0.1

Hotfix (non-breaking bug):

* Changed warning action for `PyotResourceWarning` to `default`.

## 6.0.0

Summary:

* Reworked resource management logic, fixing a scary amount of issues and bugs related to ungraceful handling of resources. Warnings and errors such as `unclosed transport ...`, `unclosed <socker._socket ...`, `Event loop is closed`, etc.
* Reworked parts of warning mechanics, warnings are now actual warnings sent using the `warnings` module instead of `logging`.
* Reworked major parts of `pyot.utils` modules, removing unused and over-abstracted codes.
* Support for `aioredis` v2.0, dropping support for `aioredis` v1.3.
* Other general codebase improvements.

New additions:

* Introduces new core resource management module with `pyot.core.resources`, intended to handle graceful acquisition and release of resources used by Pyot, the usage is **recommended but optional**, please refer to **Cores -> Resources**.
* New conf utils function `import_confs` for importing pyot conf files, located at `pyot.conf.utils`. It's a slightly modified version of `importlib.import_module`.
* New functools decorator `async_generator_property`, for decorating an async generator method to a property.
* Most warnings are now sent using `warnings.warn`, new module `pyot.core.warnings` contain all warning classes used by Pyot. Stores log level remains using `logging.log`.
* New module `pyot.utils.aiohttp` with class `SafeClientSession`, intended to fix part of the ungraceful closing of resources by `aiohttp` until the release of `aiohttp>=4.0` (version where aiohttp fixes it themselves).

Breaking changes:

(To obtain the source of removed or before change codes, please search on the github commit history.)

* Removed `pyot.utils.runtime` module, with the new introduced resource management rework, the intended function of the module is no longer needed. To obtain the source code for other uses, please search on the github commit history.
* Removed `pyot.utils.eventloop` module, replaced by the new resource management module, the old 'magic' has proven to be problematic and unreliable. The near-replacement is `ResourceTemplate` -> `EventLoopFactory` and `ResourceManager` remains the same name.
* Renamed multiple internal methods and functions to `_` prefixed as they are not intended to be used publicly, its changes will no longer be documented as they are private. Including but not limited to:
  * PyotCore internal methods.
  * Stores and Limiters attributes.
* Removed `.pipeline(name: str)` method and `pipeline: str` arguments in request methods from PyotCore instances, it is now replaced with `.using(pipeline_name: str)` method. A typical non-default-pipeline request would now look like `await UnknownPyotCore(param1=param1).using(pipeline_name).get()`.
* PyotCore instances now returns `AttributeError` if `.query()` or `.body()` method is not implemented on its classes.
* Renamed `clean` method in PyotCore to `validate`.
* Removed import hook `PYOT_SETTINGS` on django settings, it has been deprecated since v5, use `PYOT_CONFS` instead.
* Removed module `pyot.utils.parsers`, for functions `to_bytes` and `from_bytes` please use `pickle.dumps` and `pickle.loads` directly. `safejson` is now a module on `pyot.utils.safejson` providing the functions `loads` and `load`.
* Removed module `pyot.utils.runners`, for `loop_run` please use `asyncio.get_event_loop().run_until_complete`, for `thread_run` please use `asyncio.get_event_loop().run_in_executor` (with `functools.partial` if needed).
* Removed module `pyot.utils.time`, use builtin module `timeit` instead.
* Renamed module `pyot.utils.locks` to `pyot.utils.threading`, class `Lock` is renamed to `AsyncLock`, the intended scope of this module is now to store `threading` related utils.
* Renamed multiple functions in modules `pyot.utils.{model}.cdragon` to be more explicit, please review on **Utils** page.
* Renamed `import_class` in `pyot.utils.importlib` to `import_variable` as it intends to import any variable in a module.
* Removed `swapped_dict` in `pyot.utils.itertools`.
* Renamed `Logger` in `pyot.utils.logging` to `LazyLogger`.
* Renamed `snakecase` in `pyot.utils.text` to `snake_case`.
* Renamed `camelcase` in `pyot.utils.text` to `camel_case`.
* Logs formatting changed from `[Trace: ...] ...` to `[pyot.{modules...}:{class}#{optional_tag}] ...`.
* Store instantiation, for stores that accepts `kwargs`, is now on first level as `**kwargs`, e.g. now accepts `"a": "b"` instead of `"kwargs": {"a": "b"}`.
* Stores and limiters depending on `aioredis` now supports `aioredis>=2.0`, due to breaking compatibility, this version will not support `aioredis<=2.0` anymore, the class names remains the **same** and no extra changes required on conf.

The following breaking changes is specific to the PyotCore class `val.Match`, the renamed attributes is to reserve them for the new static asset bridges (much like `lol.Champion`, `lol.Item`, etc.) **to be released in the next versions**.

* Renamed `val.match.MatchInfoData.map_id` to `val.match.MatchInfoData.map_url`.
* Renamed `val.match.MatchPlayerData.player_card` to `val.match.MatchPlayerData.player_card_id`.
* Renamed `val.match.MatchPlayerData.player_title` to `val.match.MatchPlayerData.player_title_id`.
* Renamed `val.match.MatchPlayerEconomyData.weapon` to `val.match.MatchPlayerEconomyData.weapon_id`.
* Renamed `val.match.MatchPlayerEconomyData.armor` to `val.match.MatchPlayerEconomyData.armor_id`.
* Attribute `val.match.MatchPlayerFinishingDamageData.damage_item` will remain unchanged, due to it being agnostic and inconsistent, subject to change.


