Package: pybrook
IntroductionPyBrook - a real-time cloud computing framework for the Internet of Things.
PyBrook enables users to define complex data processing models declaratively using the Python programming language.
The framework also provides a generic web interface that presents the collected data in real-time.PyBrook aims to make the development of real-time data processing services as easy as possible by utilising powerful
mechanisms of the Python programming language and modern concepts like hot-reloading or deploying software in Linux Containers.A simpledocker-compose upis enough to start playing with the framework.Run demo with DockerIt is recommended to usedocker-composefor learning (you can use thedocker-compose.ymlfrom theproject repository:docker-composeupThis command will start all the services, including Redis with Redis Gears enabled.The following services will be available:OpenAPI docs (ReDoc):http://localhost:8000/redocOpenAPI docs (Swagger UI):http://localhost:8000/docsPyBrook frontend:http://localhost:8000/panelLocust panel for load testing:http://localhost:8089You should probably visit the Locust panel first and start sending some reports.Using your own modelThe configured model ispybrook.examples.demo, but replacing it with your own is very easy.First, you have to save your custom model somewhere.
For now, you can just copy the source ofpybrook.examples.demo(attached below) and save it asmymodel.pyin your working directory.??? example "Source ofpybrook.examples.demo"```python linenums="1"
from datetime import datetime
from math import atan2, degrees
from typing import Optional, Sequence

from pybrook.models import (
    InReport,
    OutReport,
    PyBrook,
    ReportField,
    dependency,
    historical_dependency,
)

brook = PyBrook('redis://localhost')
app = brook.app


@brook.input('ztm-report', id_field='vehicle_number')
class ZTMReport(InReport):
    vehicle_number: int
    time: datetime
    lat: float
    lon: float
    brigade: str
    line: str


@brook.output('location-report')
class LocationReport(OutReport):
    vehicle_number = ReportField(ZTMReport.vehicle_number)
    lat = ReportField(ZTMReport.lat)
    lon = ReportField(ZTMReport.lon)
    line = ReportField(ZTMReport.line)
    time = ReportField(ZTMReport.time)
    brigade = ReportField(ZTMReport.brigade)


@brook.artificial_field()
def direction(lat_history: Sequence[float] = historical_dependency(
    ZTMReport.lat, history_length=1),
                    lon_history: Sequence[float] = historical_dependency(
                        ZTMReport.lon, history_length=1),
                    lat: float = dependency(ZTMReport.lat),
                    lon: float = dependency(ZTMReport.lon)) -> Optional[float]:
    prev_lat, = lat_history
    prev_lon, = lon_history
    if prev_lat and prev_lon:
        return degrees(atan2(lon - prev_lon, lat - prev_lat))
    else:
        return None


@brook.output('direction-report')
class DirectionReport(OutReport):
    direction = ReportField(direction)


@brook.artificial_field()
async def counter(prev_values: Sequence[int] = historical_dependency(
    'counter', history_length=1),
                  time: datetime = dependency(ZTMReport.time)) -> int:
    prev_value, = prev_values
    if prev_value is None:
        prev_value = -1
    prev_value += 1
    return prev_value


@brook.output('counter-report')
class CounterReport(OutReport):
    counter = ReportField(counter)


brook.set_meta(latitude_field=LocationReport.lat,
               longitude_field=LocationReport.lon,
               time_field=LocationReport.time,
               group_field=LocationReport.line,
               direction_field=DirectionReport.direction)

if __name__ == '__main__':
    brook.run()
```After creatingmymodel.py, you should add it to theapiandworkercontainers, using a Docker volume.
To make PyBrook usemymodelinstead ofpybrook.examples.demo, you should also alter the arguments passed togunicornandpybrook.
You can simply add it to the defaultdocker-compose.yml:services:api:image:pybrook:latestbuild:context:.environment:REDIS_URL:redis://redisports:-8000:8000volumes:-./mymodel.py:/src/mymodel.pycommand:gunicorn mymodel:app-w 4 -k uvicorn.workers.UvicornWorker-b 0.0.0.0:8000worker:image:pybrook:latestdepends_on:-apienvironment:REDIS_URL:redis://redisDEFAULT_WORKERS:8volumes:-./mymodel.py:/src/mymodel.pycommand:pybrook mymodel:brooklocust:image:pybrook:latestdepends_on:-apiports:-8089:8089command:locust -H http://api:8000redis:image:redislabs/redisgears:1.0.9Then rundocker-compose up --buildagain, to start PyBrook - this time using your own model.Setup & DevelopmentYou can install the PyBrook from PyPi usingpip:pipinstallpybrookRunning all services manually, without DockerTo run thepybrook.examples.demomodel, you have to start all the required services manually:# Redis + Redis Gearsdockerrun--net=host-dredislabs/redisgears:1.0.9# HTTP API based on pybrook.examples.demo - uvicornuvicornpybrook.examples.demo:app--reload# PyBrook workers based on pybrook.examples.demopybrookpybrook.examples.demo:brook-rg# Locust - load testinglocust-Hhttp://localhost:8000ContributingPyBrook usespoetryfor dependency management.
To install all its development dependencies, simply run this command:poetryinstallTestsmaketestCode qualityThe source code of PyBrook is formatted using yapf and isort.To run them with the correct settings, use the following command:makeformatPyBrook usesmypyfor type checking andflake8for linting.
Use the following command to run them with the appropriate settings:makelint