Skip to main content

Back office low code

Project description

Back Office Low Code (backo)

release

pylint test

Backo

The way to use is very simple, see Quickstart for a basic setup.

What is backo

Installation

pip install backo # (soon)

Quickstart

Here is a sample with a DB (storage full in yaml file) with users and adresses reference

erDiagram
    User }o--|| Adresses : addr
    User {
        string name
        string surname
        bool male
        ref addr
    }
    Adresses {
        int orderNumber
        string name
        string adresse
        refs users
    }

Becomes in python with backo

from flask import Flask
from backo import Item, DBYmlConnector, Backoffice
from backo import Ref, RefsList, DeleteStrategy

# --- Storage for user
yml_users = DBYmlConnector(path="/tmp")
# --- Storage for addresses
yml_addr = DBYmlConnector(path="/tmp")

# -- Description of the backoffice
my_backoffice = Backoffice("myApp")

# -- Add the collection "users" into this backoffice (with a reference to the futur "addrs" collection)
my_backoffice.add_collection(
    "users",
    Item(
        {
            "name": String(),
            "surname": String(),
            "addr": Ref(coll="addrs", field="$.users", required=True),
            "male": Bool(default=True),
        },
        yml_users,
    ),
)
# -- Add the collection "addrs" into this backoffice (with reference to the "usres" collection)
my_backoffice.add_collection(
    "addrs",
    Item(
        {
            "name": String(),
            "address": String(),
            "users": RefsList(
                coll="users", field="$.addr", ods=DeleteStrategy.MUST_BE_EMPTY
            ),
        },
        yml_addr,
    ),
)


# Your flask application
flask = Flask(__name__)

# -- Add CRUD routes for this application
my_backoffice.add_routes(flask)

Syntax

backo use stricto for structured description language.

Item

Itemis the main object in backo. It describe an object in the DB with all methodes for CRUD (CReate, Update, Delete).

A generic object is a stricto Dict() object.

Item( description object , db connector )

example :

# Describe what is a 'cat'
cat = Item(
        {
            "name": String( required=True, default='Felix'),
            "address": String(),
            "age" : Int()
        },
        db_connector_for_cat)

# Add the cat object into the backoffice object
backoffice.add_collection( "cats", cat )
# similar : backoffice.register_collection( "cats", cat )

[!IMPORTANT]
At this point, you don't care about _id.

 Method  Description
.create( data :dict ) Create an Item into the DB with data in parameters.
.save() save the Item into the DB
.load( _id :str ) get the Item with the _id from the DB
.reload() reload the Item from the DB
.delete() delete the Item in the DB
.new() create an empty Item (mist be fill with .set() and then .save()
.select() do a selection of Items from the DB retirn a list of Item

each function raise errors in something goes wrong

Ref and RefsList

A Ref() is a specific type for relation between collections ( aka tables).

Ref one to many

this is an example with books and authors

# Authors write books
author = Item({
    'name' : String(),
    'books' : RefsList( coll='book', field="$.autor" )
}, db_connector)

# A book is written by on author
book = Item({
    ... # Some attibutes
    # one or zero to many
    author = Ref( coll='author', field="$.books" )
    # or one to many
    author = Ref( coll='author', field="$.books", required=True )
}, db_connector )
Option for Ref Default Description
coll= None the collection to make the ref
table= None similar to coll
field= None The reverse field in the targeted collection (use selector to target it)
rev= None similar to field
ods= None On Delete Strategy see ods

And all options availables in stricto String() fields.

On Delete Strategy (ods)

ods define the behaviour of the database when a delete occure and the object contain some RefList. For each RefList, you can define the strategy :

  • DeleteStrategy.MUST_BE_EMPTY (by default) This strategy oblige this RefList to be empty first. Otherwise, the delete wil be refused and an Error will be raised.

  • DeleteStrategy.DELETE_REVERSES_TOO This strategy delete all reverse object too. can be dangerous.

  • DeleteStrategy.CLEAN_REVERSES This strategy is often used in many-to-many links. This strategy erase this reference on the reverse object

Routes

Automatic creation route provide the following routes.

GET /coll/<collection name>/<_id> ?_view=<view name>

_view are defined in stricto views

Return the object of this collection by _id.

curl -X GET 'http://localhost/myApp/coll/users/123'

# Equivalent (by default _view=client)
curl -X GET 'http://localhost/myApp/coll/users/123?_view=client'

# Another view
curl -X GET 'http://localhost/myApp/coll/users/123?_view=otherviewname'

Answers can be :

code  data  Description
 200  json structure of the object  the object
401 None  you don t have the righe to see this element
404 None Element does not exist
500 None Crash from the server

GET /coll/<collection name>?<query_string>

Get a list of objects matching the query string. The query string can be with this format

key value description
<field> <value> example surname=donald mean i want to select all donald's
<field>.<operator> <value> example age.$lt=12 mean the age field must be less than 12
<field>.<subfield> <value> example address.number=1 mean address is a nested Dict with number attribut

list of available operators

key value description
_view string ( by default "client" ) example _view=short is the selection of view. stricto views
_page int example _page=50 mean the number of element to get
_skip int example _skip=100 mean to start at the element 100 of the list. it is for pagination.

The result (if no error) is a http 200 with this json object :

{
    "result": # list of dict containing objects matched
    "total": # (int) total number of object matched
    "_view": # the _view given in the request
    "_skip": # the _skip given in the request
    "_page": # the _page given in the request
}

examples

curl -X GET 'http://localhost/myApp/coll/users/?name.$re=do&_page=10'  # All users name matching "do" 10 per page.

POST /coll/<collection name>

Create a new object.

curl -X POST 'http://localhost/myApp/coll/users/' -d '{"name":"John","surname":"Rambo"}'

return the created user object with this _id and some _metadatas, or an error

PUT /coll/<collection name>/<_id>

Modify an existing object.

curl -X PUT 'http://localhost/myApp/coll/users/1234' -d '{"name":"Johnny"}'

Modify the users with _id 1234 and return the modified object.

DELETE /coll/<collection name>/<_id>

Delete an existing object.

curl -X DELETE 'http://localhost/myApp/coll/users/1234'

Delete the users with _id 1234.

PATCH /coll/<collection name>/<_id>

Modify an existing object with patch method.

curl -X PATCH  'http://localhost/myApp/coll/users/1234' -d '{"op": "replace", "path" : "$.name", "value": "Gilda"}'

Modifythe users with _id 1234 with the patch.

Patch content can be a list of patch operations

Internal usage

Then usage :

# Create an adress, save it in the DB and provide the object an uniq id.
moon_address = backoffice.addrs.create({"name": "moon", "address": "far"})

# Create a user with this address
astro = backoffice.users.create({"name": "neil", "surname": "amstrong", "addr": moon_address._id})

moon_address.users # -> return []
moon_address.reload() # reload datas from the DB.
moon_address.users # -> return [ astro._id ]

_id

You dont't hate to care of _ids in your item description. Backo will alter schema to add _id for each Item (see stricto schemas for details).

_meta

the db_connector add meta datas ti each item by altering schema.

The provided meta_data_handler give this Dict() :

Dict(
    {
        "ctime": Datetime(),
        "mtime": Datetime(),
        "created_by": Dict(
            {"user_id": String(), "login": String()}
        ),
        "modified_by": Dict(
            {"user_id": String(), "login": String()}
        ),
    },
),

With associated rights.

workflow and events

Each Item has a specific workflow and trigg specific events

workflow

Here is states fir each Item

 State  descripion
 UNSET  The Item result of a .new() function. cannot be saved in this state
UNSAVED  The Item has some changes and must be saved
SAVED  The Item is saved in the DB and no changes since the last save
stateDiagram
    [*] --> UNSET : .new()

    UNSET --> SAVED : .load()
    UNSET --> UNSAVED : .set()
    UNSAVED --> SAVED : .save()
    SAVED --> UNSAVED : .set()
    SAVED --> UNSET : .delete()
    UNSAVED --> UNSET : .delete()

events

Some events are trigged during some functions of the Item.

function  event before  event after
.load()  "loaded"
.save() "before_save"  "saved"
.delete() "before_delete"
.create() None  "created"

You can use them

        def rip( event_name, root, me, **kwargs ):
            """
            event_name = "before_delete"
            root = cat Item
            me = cat Item too
            """
            # Do what you want


        cat = Item( {
            'name' : String()
            'birth' : Datetime()
        }
        on=[ ( "before_delete", rip ) ]
        )

Transactions

Soon

CurrentUser

Soon

Logs

Log system is based on logging

You must first design your logging system with handlers. Then write logs.

sample use

import logging
from backo import log_system

# To write all file to stderr
log_system.add_handler( log_system.set_streamhandler() )

# To write in a file
log_system.add_handler( log_system.set_filehandler("/var/log/mylog.log") )

# Set the level 
log_system.setLevel( logging.INFO )

# create your own sub logger with its specific logging level
log = log_system.get_or_create_logger("custom")
log.setLevel(loggind.DEBUG)

log.debug("hey this is my first debug message")

advanced use

You can select a specific logger and modify it by adding/removing handlers and and changing its level.

log = log_system.get_or_create_logger("custom")
log.setLevel(loggind.DEBUG)
log.addHandler ( my_own_handler )
# ...

current loggers

Current availables loggers are :

 logger   description
backoffice The main Backoffice system
Item  The database itself (CRUD operations )
ref Ref and RefsList objects
transaction  transactions and roolback
yml yaml database connector

Tests & co

For personal use only

# all tests
python -m unittest tests
# or for a specific test
python -m unittest tests.TestDict.test_simple_type

# reformat
python -m black .

# pylint
pylint $(git ls-files '*.py')

# coverage
coverage run -m unittest tests
coverage html # report under htmlcov/index.html
firefox htmlcov/index.html

Building a new release

For personal use only

# Modify changelog
# modify pyproject.toml
git add -u
git commit -am 'preparing 0.0.x'
git push
git tag -a 0.0.x -m '0.0.x'
git push origin tag 0.0.x

# publish a new relase in github interface, based on tag 

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

backo-0.0.1.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

backo-0.0.1-py3-none-any.whl (31.5 kB view details)

Uploaded Python 3

File details

Details for the file backo-0.0.1.tar.gz.

File metadata

  • Download URL: backo-0.0.1.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for backo-0.0.1.tar.gz
Algorithm Hash digest
SHA256 f2bcbe0c144176bdc212ac4b4a4af11aad26b3ba235ecc6cf48b551dc3e6ea56
MD5 59569f9611a26cbfc252932b863055c2
BLAKE2b-256 32f980bcb9d90269575d6f39565be6dd111d24476e2b8c08f0d00f2aabaa60df

See more details on using hashes here.

File details

Details for the file backo-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: backo-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 31.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for backo-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9ff91d54a1ab16453766841901294e1156cef97ad303346b6837503c32c1685e
MD5 f66c09098c12e03f6fc309a39b31e56e
BLAKE2b-256 b98a784b3d7394b027740e23a0e5896098061216c2312af171634007b0ce28f4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page