Add inventories

This commit is contained in:
Xavier Bustamante Talavera 2019-01-21 16:08:55 +01:00
parent f0f1376b7d
commit 9272674760
11 changed files with 50 additions and 13 deletions

View File

@ -64,10 +64,15 @@ class DevicehubConfig(Config):
TAG_TOKEN = None TAG_TOKEN = None
"""Access to the tag provider.""" """Access to the tag provider."""
def __init__(self, db: str = None) -> None: def __init__(self, schema: str = None, token=None) -> None:
if not self.ORGANIZATION_NAME or not self.ORGANIZATION_TAX_ID: if not self.ORGANIZATION_NAME or not self.ORGANIZATION_TAX_ID:
raise ValueError('You need to set the main organization parameters.') raise ValueError('You need to set the main organization parameters.')
if not self.TAG_BASE_URL or not self.TAG_TOKEN: if not self.TAG_BASE_URL:
raise ValueError('You need a tag service.') raise ValueError('You need a tag service.')
self.TAG_TOKEN = token or self.TAG_TOKEN
if not self.TAG_TOKEN:
raise ValueError('You need a tag token')
self.TAG_BASE_URL = boltons.urlutils.URL(self.TAG_BASE_URL) self.TAG_BASE_URL = boltons.urlutils.URL(self.TAG_BASE_URL)
super().__init__(db) if schema:
self.SCHEMA = schema
super().__init__()

View File

@ -1,3 +1,4 @@
import citext
from sqlalchemy import event from sqlalchemy import event
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
from sqlalchemy.sql import expression from sqlalchemy.sql import expression
@ -11,7 +12,10 @@ class SQLAlchemy(SchemaSQLAlchemy):
schema of the database, as it is in the `search_path` schema of the database, as it is in the `search_path`
defined in teal. defined in teal.
""" """
# todo add here all types of columns used so we don't have to
# manually import them all the time
UUID = postgresql.UUID UUID = postgresql.UUID
CIText = citext.CIText
def drop_all(self, bind='__all__', app=None): def drop_all(self, bind='__all__', app=None):
"""A faster nuke-like option to drop everything.""" """A faster nuke-like option to drop everything."""
@ -37,6 +41,6 @@ def create_view(name, selectable):
return table return table
db = SQLAlchemy(session_options={"autoflush": False}) db = SQLAlchemy(session_options={'autoflush': False})
f = db.func f = db.func
exp = expression exp = expression

View File

@ -44,9 +44,11 @@ class Devicehub(Teal):
# todo can I make it with a global Session only? # todo can I make it with a global Session only?
event.listen(db.session, 'before_commit', DeviceSearch.update_modified_devices) event.listen(db.session, 'before_commit', DeviceSearch.update_modified_devices)
def _init_db(self): def _init_db(self, exclude_schema=None, check=False):
super()._init_db() created = super()._init_db(exclude_schema, check)
if created:
DeviceSearch.set_all_devices_tokens_if_empty(self.db.session) DeviceSearch.set_all_devices_tokens_if_empty(self.db.session)
return created
def regenerate_search(self): def regenerate_search(self):
"""Re-creates from 0 all the search tables.""" """Re-creates from 0 all the search tables."""

View File

@ -46,7 +46,7 @@ class OrganizationDef(AgentDef):
print(json.dumps(o, indent=2)) print(json.dumps(o, indent=2))
return o return o
def init_db(self, db: SQLAlchemy): def init_db(self, db: SQLAlchemy, exclude_schema=None):
"""Creates the default organization.""" """Creates the default organization."""
org = models.Organization(**app.config.get_namespace('ORGANIZATION_')) org = models.Organization(**app.config.get_namespace('ORGANIZATION_'))
db.session.add(org) db.session.add(org)

View File

@ -297,6 +297,7 @@ class ManufacturerDef(Resource):
SCHEMA = schemas.Manufacturer SCHEMA = schemas.Manufacturer
AUTH = True AUTH = True
def init_db(self, db: 'db.SQLAlchemy'): def init_db(self, db: 'db.SQLAlchemy', exclude_schema=None):
"""Loads the manufacturers to the database.""" """Loads the manufacturers to the database."""
if exclude_schema != 'common':
Manufacturer.add_all_to_session(db.session) Manufacturer.add_all_to_session(db.session)

View File

@ -0,0 +1,12 @@
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.models import Thing
class Inventory(Thing):
__table_args__ = {'schema': 'common'}
id = db.Column(db.Unicode(), primary_key=True)
id.comment = """The name of the inventory as in the URL and schema."""
name = db.Column(db.CIText(), nullable=False, unique=True)
name.comment = """The human name of the inventory."""
tag_token = db.Column(db.UUID(as_uuid=True), unique=True)
tag_token.comment = """The token to access a Tag service."""

View File

@ -34,7 +34,7 @@ class LotDef(Resource):
view_func=lot_device, view_func=lot_device,
methods={'POST', 'DELETE'}) methods={'POST', 'DELETE'})
def init_db(self, db: 'db.SQLAlchemy'): def init_db(self, db: 'db.SQLAlchemy', exclude_schema=None):
# Create functions # Create functions
with pathlib.Path(__file__).parent.joinpath('dag.sql').open() as f: with pathlib.Path(__file__).parent.joinpath('dag.sql').open() as f:
sql = f.read() sql = f.read()

View File

@ -5,6 +5,8 @@ from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy_utils import EmailType, PasswordType from sqlalchemy_utils import EmailType, PasswordType
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.inventory.model import Inventory
from ereuse_devicehub.resources.models import STR_SIZE, Thing from ereuse_devicehub.resources.models import STR_SIZE, Thing
@ -23,6 +25,10 @@ class User(Thing):
data_types.html#module-sqlalchemy_utils.types.password>`_ data_types.html#module-sqlalchemy_utils.types.password>`_
""" """
token = Column(UUID(as_uuid=True), default=uuid4, unique=True) token = Column(UUID(as_uuid=True), default=uuid4, unique=True)
inventories = db.relationship(Inventory,
backref=db.backref('users', lazy=True, collection_class=set),
secondary=lambda: UserInventory.__table__,
collection_class=set)
def __repr__(self) -> str: def __repr__(self) -> str:
return '<User {0.email}>'.format(self) return '<User {0.email}>'.format(self)
@ -31,3 +37,10 @@ class User(Thing):
def individual(self): def individual(self):
"""The individual associated for this database, or None.""" """The individual associated for this database, or None."""
return next(iter(self.individuals), None) return next(iter(self.individuals), None)
class UserInventory(db.Model):
"""Relationship between users and their inventories."""
__table_args__ = {'schema': 'common'}
user_id = db.Column(db.UUID(as_uuid=True), db.ForeignKey(User.id), primary_key=True)
inventory_id = db.Column(db.Unicode(), db.ForeignKey(Inventory.id), primary_key=True)

View File

@ -25,7 +25,7 @@ requests==2.19.1
requests-mock==1.5.2 requests-mock==1.5.2
SQLAlchemy==1.2.14 SQLAlchemy==1.2.14
SQLAlchemy-Utils==0.33.6 SQLAlchemy-Utils==0.33.6
teal==0.2.0a32 teal==0.2.0a33
webargs==4.0.0 webargs==4.0.0
Werkzeug==0.14.1 Werkzeug==0.14.1
sqlalchemy-citext==1.3.post0 sqlalchemy-citext==1.3.post0

View File

@ -29,7 +29,7 @@ setup(
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
install_requires=[ install_requires=[
'teal>=0.2.0a32', # teal always first 'teal>=0.2.0a33', # teal always first
'click', 'click',
'click-spinner', 'click-spinner',
'ereuse-utils[Naming]>=0.4b14', 'ereuse-utils[Naming]>=0.4b14',