From 2fbafa76044ecbe27ca8b963cceb91c2e1f64f9e Mon Sep 17 00:00:00 2001 From: dkrasner Date: Fri, 13 May 2022 19:40:48 +0200 Subject: [PATCH 1/6] fixing typo in docs/examples/consumer.py --- docs/examples/consumer.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/examples/consumer.py b/docs/examples/consumer.py index bb1a58e75..98c7ab568 100644 --- a/docs/examples/consumer.py +++ b/docs/examples/consumer.py @@ -3,19 +3,20 @@ schema = Schema("hello_world") + @schema.define class Message: timestamp = float message = str -db = connect('localhost', 8000, 'TOKEN') + +db = connect("localhost", 8000, "TOKEN") db.subscribeToSchema(schema) ts = 0 while True: - with db.view(): - Message(timestamp=time.time(), - message='message_5') + with db.transaction(): + Message(timestamp=time.time(), message="message_5") messages = Message.lookupAll() for m in messages: if m.timestamp > ts: From ee3855e55e23952af512b75247b6b294dd9a97f9 Mon Sep 17 00:00:00 2001 From: dkrasner Date: Fri, 13 May 2022 21:41:10 +0200 Subject: [PATCH 2/6] adding a cells.md introduction to cells file --- docs/cells.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/cells.md diff --git a/docs/cells.md b/docs/cells.md new file mode 100644 index 000000000..6ef7bbd55 --- /dev/null +++ b/docs/cells.md @@ -0,0 +1,70 @@ +### Cells ### + +Cells represents an abstraction of the web, tightly coupled with object database, which allows you to create "reactive" interface in python.... + + +### Preamble ### + +In order to get ourselves up and running we'll need a few things: + +* Object Database installed (see the [installation docs](../INSTALLATION.md) for more details) +* an object_database engine service instance (see [here](./object_engine.md) for more information) +* a configured and installed web service instance, which will be responsible for building and serving the web application +* and an installed instance of the service we'd like to run + +After this we can start up our web server and service. + +Lets walk through it (as in the other examples we'll use "TOKEN" for our special token): + +In a python virtual environment boot up Object Database engine like so: +``` + object_database_service_manager \ + localhost \ + localhost \ + 8000 \ + Master \ + --run_db \ + --service-token TOKEN \ + --source ./odb/source \ + --storage ./odb/storage +``` + +In another python virtual environment instance run the following: +``` +# export our special Object Database token +export ODB_AUTH_TOKEN=TOKEN + +# install the ActiveWebService +object_database_service_config install \ +--class object_database.web.ActiveWebService.ActiveWebService \ +--placement Master + +# configure ActiveWebService +object_database_service_config configure ActiveWebService \ +--port 8080 --hostname localhost --internal-port 8081 + +# check to make sure it is listed +object_database_service_config list + +# start it up +object_database_service_config start ActiveWebService + +# check to see that it is running +object_database_service_config instances +``` + +NOTE: you can always open `http://localhost:8080/` in your browser to see the running services and click to see what they are. + +#### Running a boring web app ### + +Run the following in your virtual environment: + +``` +object_database_service_config install --class object_database.service_manager.ServiceBase.ServiceBase --placement Master + +object_database_service_config instances +``` + +(NOTE: you might need to change the paths to the [ServiceBase](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/service_manager/ServiceBase.py) file depending on the directory you are running this from.) + +If you head to `http://localhost:8080/services/ServiceBase` you will see our really boring service. The simple text holder card is what [ServiceBase.serviceDisplay](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/service_manager/ServiceBase.py#L67) returns. In the next example, we'll subclass `ServiceBase` and change this method to do something more interesting. From 5f10af88fdcfe025eea787ff5dc3034630b1bcf1 Mon Sep 17 00:00:00 2001 From: dkrasner Date: Sat, 21 May 2022 11:56:06 +0200 Subject: [PATCH 3/6] linting --- docs/cells.md | 9 ++++++--- docs/examples/aws_start.sh | 22 ++++++++++++++++++++++ docs/examples/cells.py | 16 ++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 docs/examples/aws_start.sh create mode 100644 docs/examples/cells.py diff --git a/docs/cells.md b/docs/cells.md index 6ef7bbd55..6c2f1947d 100644 --- a/docs/cells.md +++ b/docs/cells.md @@ -53,9 +53,9 @@ object_database_service_config start ActiveWebService object_database_service_config instances ``` -NOTE: you can always open `http://localhost:8080/` in your browser to see the running services and click to see what they are. +NOTE: you can always open [http://localhost:8080/](http://localhost:8080/) in your browser to see the running services and click to see what they are. Also, if you get tired of running the above commands, there is a small bash script found [here](./examples/aws_start.sh). -#### Running a boring web app ### +#### Running a boring web app #### Run the following in your virtual environment: @@ -67,4 +67,7 @@ object_database_service_config instances (NOTE: you might need to change the paths to the [ServiceBase](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/service_manager/ServiceBase.py) file depending on the directory you are running this from.) -If you head to `http://localhost:8080/services/ServiceBase` you will see our really boring service. The simple text holder card is what [ServiceBase.serviceDisplay](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/service_manager/ServiceBase.py#L67) returns. In the next example, we'll subclass `ServiceBase` and change this method to do something more interesting. +If you head to [http://localhost:8080/services/ServiceBase](http://localhost:8080/services/ServiceBase) you will see our really boring service. The simple text holder card is what [ServiceBase.serviceDisplay](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/service_manager/ServiceBase.py#L67) returns. In the next example, we'll subclass `ServiceBase` and change this method to do something more interesting. + + +#### Running a more interesting web app #### diff --git a/docs/examples/aws_start.sh b/docs/examples/aws_start.sh new file mode 100644 index 000000000..e17134a7f --- /dev/null +++ b/docs/examples/aws_start.sh @@ -0,0 +1,22 @@ +## Active Web Service startup script + +# export our special Object Database token +export ODB_AUTH_TOKEN=TOKEN + +# install the ActiveWebService +object_database_service_config install \ +--class object_database.web.ActiveWebService.ActiveWebService \ +--placement Master + +# configure ActiveWebService +object_database_service_config configure ActiveWebService \ +--port 8080 --hostname localhost --internal-port 8081 + +# check to make sure it is listed +object_database_service_config list + +# start it up +object_database_service_config start ActiveWebService + +# check to see that it is running +object_database_service_config instances diff --git a/docs/examples/cells.py b/docs/examples/cells.py new file mode 100644 index 000000000..5d9254d47 --- /dev/null +++ b/docs/examples/cells.py @@ -0,0 +1,16 @@ +import object_database.web.cells as cells +from object_database import ServiceBase + + +class SomethingMoreInteresting(ServiceBase): + def initialize(self): + self.buttonName = "click me" + return + + @staticmethod + def serviceDisplay(serviceObject, instance=None, objType=None, queryArgs=None): + return cells.Card(cells.button("Click me", onClick)) + + +def onClick(): + return "thanks" From ed629c2c1f5150f1642094c814e1fabab7bcdac8 Mon Sep 17 00:00:00 2001 From: dkrasner Date: Thu, 2 Jun 2022 13:52:03 +0200 Subject: [PATCH 4/6] Update cells.md updating cells.md docs npm install/bundle build instructions minor correction to basic service install --- docs/cells.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/cells.md b/docs/cells.md index 6c2f1947d..f63a8915d 100644 --- a/docs/cells.md +++ b/docs/cells.md @@ -9,6 +9,10 @@ In order to get ourselves up and running we'll need a few things: * Object Database installed (see the [installation docs](../INSTALLATION.md) for more details) * an object_database engine service instance (see [here](./object_engine.md) for more information) +* build the bundle for the services landing page + * `cd object_database/object_database/web/content` + * create a node environment and source it + * run `npm install && npm run build` * a configured and installed web service instance, which will be responsible for building and serving the web application * and an installed instance of the service we'd like to run @@ -61,7 +65,11 @@ Run the following in your virtual environment: ``` object_database_service_config install --class object_database.service_manager.ServiceBase.ServiceBase --placement Master - +# check to see it is installed +object_database_service_config list +# start +object_database_service_config start ServiceBase +# check to see it is running object_database_service_config instances ``` From d63e4c09cd6fc9ea182000b1a493e0305dc5b41a Mon Sep 17 00:00:00 2001 From: dkrasner Date: Thu, 2 Jun 2022 15:26:58 +0200 Subject: [PATCH 5/6] wrapping up examples/cells.py and adding examples/cells_odb.py --- docs/examples/__init__.py | 0 docs/examples/cells.py | 14 ++++--- docs/examples/cells_odb.py | 81 ++++++++++++++++++++++++++++++++++++++ quickstart/Dockerfile | 11 ++++-- 4 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 docs/examples/__init__.py create mode 100644 docs/examples/cells_odb.py diff --git a/docs/examples/__init__.py b/docs/examples/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/examples/cells.py b/docs/examples/cells.py index 5d9254d47..3cb393d8f 100644 --- a/docs/examples/cells.py +++ b/docs/examples/cells.py @@ -9,8 +9,12 @@ def initialize(self): @staticmethod def serviceDisplay(serviceObject, instance=None, objType=None, queryArgs=None): - return cells.Card(cells.button("Click me", onClick)) - - -def onClick(): - return "thanks" + return cells.Card( + cells.Panel( + cells.Button("Reload", lambda: "") + + cells.Button("Service Base", lambda: "ServiceBase") + + cells.Button("Active Web Service", lambda: "ActiveWebService") + ), + header="This is a 'card' cell with some buttons", + padding="10px" + ) diff --git a/docs/examples/cells_odb.py b/docs/examples/cells_odb.py new file mode 100644 index 000000000..8512d90a6 --- /dev/null +++ b/docs/examples/cells_odb.py @@ -0,0 +1,81 @@ +from object_database import Schema, ServiceBase +import object_database.web.cells as cells +import time +import random + +# define a 'schema', which is a collection of classes we can subscribe to as a group +schema = Schema("cells_obd") + +# define a type of entry in odb. We'll have one instance of this class for each +# message in the database +@schema.define +class Message: + timestamp = float + message = str + lifetime = float + + +class AnODBService(ServiceBase): + def initialize(self): + # make sure we're subscribed to all objects in our schema. + self.db.subscribeToSchema(schema) + + def doWork(self, shouldStop): + # this is the main entrypoint for the service - it gets to do work here. + while not shouldStop.is_set(): + #wake up every 100ms and look at the objects in the ODB. + time.sleep(.1) + + # delete any messages more than 10 seconds old + with self.db.transaction(): + # get all the messages + messages = Message.lookupAll() + + for m in messages: + if m.timestamp < time.time() - m.lifetime: + # this will actually delete the object from the ODB. + m.delete() + + @staticmethod + def serviceDisplay(serviceObject, instance=None, objType=None, queryArgs=None): + # make sure cells has loaded these classes in the database and subscribed + # to all the objects. + cells.ensureSubscribedSchema(schema) + + def newMessage(): + # calling the constructor creates a new message object. Even though we + # orphan it immediately, we can always get it back by calling + # Message.lookupAll() + # because ODB objects have an explicit lifetime (they have to be destroyed) + Message(timestamp=time.time(), message=editBox.currentText.get(), lifetime=20) + + # reset our edit box so we can type again + editBox.currentText.set("") + + # define an 'edit box' cell. The user can type into this. + editBox = cells.SingleLineTextBox(onEnter=lambda newText: newMessage()) + + return cells.Panel( + editBox >> cells.Button( + "New Message", + newMessage + ) + ) + ( + cells.Table( + colFun=lambda: ['timestamp', 'lifetime', 'message'], + rowFun=lambda: sorted(Message.lookupAll(), key=lambda m: -m.timestamp), + headerFun=lambda x: x, + rendererFun=lambda m, col: cells.Subscribed( + lambda: + cells.Timestamp(m.timestamp) if col == 'timestamp' else + m.message if col == 'message' else + cells.Dropdown( + m.lifetime, + [1, 2, 5, 10, 20, 60, 300], + lambda val: setattr(m, 'lifetime', val) + ) + ), + maxRowsPerPage=100, + fillHeight=True + ) + ) diff --git a/quickstart/Dockerfile b/quickstart/Dockerfile index 5116d5ba5..46e0d3bd8 100644 --- a/quickstart/Dockerfile +++ b/quickstart/Dockerfile @@ -1,17 +1,20 @@ # Using 3.8 because typed_python doesn't seem to # compile with 3.9 (as of 2022-04-27) -FROM python:3.8-slim as builder +FROM python:3.9-slim as builder -ADD . /opt/object_database +ADD ./object_database /opt/object_database +ADD ./typed_python /opt/typed_python RUN apt update -y -qq \ && apt upgrade -y -qq \ + && apt-get update \ && apt install -y -qq curl libssl-dev build-essential git npm \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN pip install --upgrade pip \ - && pip install typed_python \ + && cd /opt/typed_python \ + && pip install -e . \ && cd /opt/object_database \ && pip install -e . @@ -19,4 +22,4 @@ RUN cd /opt/object_database/object_database/web/content \ && npm install \ && npm run build -CMD ["object_database_webtest"] \ No newline at end of file +CMD ["object_database_webtest"] From 4f7279e8c95f0c478bec338a550f83d377604aea Mon Sep 17 00:00:00 2001 From: dkrasner Date: Thu, 2 Jun 2022 15:41:18 +0200 Subject: [PATCH 6/6] Update cells.md Wrapping up cells.md more examples and ODB example --- docs/cells.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/cells.md b/docs/cells.md index f63a8915d..2def0e57e 100644 --- a/docs/cells.md +++ b/docs/cells.md @@ -79,3 +79,38 @@ If you head to [http://localhost:8080/services/ServiceBase](http://localhost:808 #### Running a more interesting web app #### + +Take a look at [cells.py]('./examples/cells.py'). You'll see we made a subclass of `ServiceBase` and overrode its `.serviceDisplay` method. There is card with a header and some buttons which all route you to the corresponding URI (in our case the list of services we have running), plus a little bit of styling. + +Lets install our more interesting app like above: +``` +object_database_service_config install --class docs.examples.cells.SomethingMoreInteresting --placement Master +object_database_service_config start SomethingMoreInteresting +``` + +Note: when you make changes to `SomethingMoreInteresting` you need to reinstall it, with the above command. If you see a message like `Cannot set codebase of locked service 'SomethingMoreInteresting'` then click the "Locked" icon in [http://localhost:8080/services]([http://localhost:8080/services) to unlock it, then reinstall. + + +You can learn more about cells by perusing the [cells](https://github.com/APrioriInvestments/object_database/tree/dev/object_database/web/cells) directory or taking a look at the [cells test example](https://github.com/APrioriInvestments/object_database/blob/dev/object_database/web/CellsTestService.py#L63) + +#### Running an ODB web app #### + +But before we wrap up, we should really build a cells examples which works with Object Database, since that's the main point here. + +I am going to skip over details of how ODB works. Please here [here](https://github.com/APrioriInvestments/object_database/blob/dev/docs/object_engine.md) for an introduction. + +We'll be building an `AnODBService` which you can find [here](https://github.com/APrioriInvestments/object_database/blob/daniel-examples/docs/examples/cells_odb.py). You'll see we need to define +* a [schema](https://github.com/APrioriInvestments/object_database/blob/daniel-examples/docs/examples/cells_odb.py#L12) +* how our app will [interact with ODB](https://github.com/APrioriInvestments/object_database/blob/daniel-examples/docs/examples/cells_odb.py#L23) +* and the [UI](https://github.com/APrioriInvestments/object_database/blob/daniel-examples/docs/examples/cells_odb.py#L40) for the app itself. + +The app will send and recieve messages from the database, and update the UI which consists largely of a Panel and Table cell. The key departure here from the previous examples is the lambda functions passed to the cells. Instead of returning something like a string (which then tells the service to route to the correponding URI), these interact with the DB via the `Message` class. + +As before we'll need to install and start the service: +Lets install our more interesting app like above: +``` +object_database_service_config install --class docs.examples.cells_db.AnODBService --placement Master +object_database_service_config start AnODBService +``` + +We'll learn more about cells and how to develop them in the upcoming `cells_dev.md` doc.