In progress: Jeeves communication is now based on SQLAlchemy
This commit is contained in:
parent
0fdc029153
commit
28726fee01
21 changed files with 637 additions and 78 deletions
|
|
@ -3,7 +3,8 @@
|
|||
pyjeeves.models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
consolodated models module
|
||||
models for databases
|
||||
"""
|
||||
from pyjeeves.connector import DBConnector
|
||||
|
||||
from .jvsmodels import * # noqa
|
||||
db = DBConnector()
|
||||
|
|
|
|||
112
pyjeeves/models/abc.py
Normal file
112
pyjeeves/models/abc.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
"""
|
||||
Define an Abstract Base Class (ABC) for models
|
||||
"""
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy.sql.expression import and_
|
||||
from sqlalchemy.orm.collections import InstrumentedList
|
||||
|
||||
from pyjeeves import logging
|
||||
|
||||
from . import db
|
||||
|
||||
logger = logging.getLogger("PyJeeves." + __name__)
|
||||
|
||||
|
||||
class RawBaseModel():
|
||||
""" Generalize __init__, __repr__ and to_json
|
||||
Based on the models columns , ForetagKod=1"""
|
||||
|
||||
print_only = () # First filter
|
||||
print_filter = () # Second filter
|
||||
to_json_filter = () # Only json filter
|
||||
column_map = {}
|
||||
|
||||
__table_args__ = {
|
||||
'extend_existing': True
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _base_filters(self, obj, filters=and_()):
|
||||
# This method provides base filtering, additional filtering can be done in subclasses
|
||||
# Add this method to your model if you want more filtering, otherwise leave it out
|
||||
# import and_ from sqlalchemy package
|
||||
# this is a base filter for ALL queries
|
||||
return and_(
|
||||
obj.ForetagKod == 1,
|
||||
filters
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
""" Define a base way to print models
|
||||
Columns inside `print_filter` are excluded """
|
||||
return '%s(%s)' % (self.__class__.__name__, {
|
||||
column: value
|
||||
for column, value in self._to_dict().items()
|
||||
if column not in self.print_filter
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def _to_json_types(value):
|
||||
if isinstance(value, datetime):
|
||||
return value.strftime('%Y-%m-%d')
|
||||
if isinstance(value, Decimal):
|
||||
return "%.2f" % value
|
||||
try:
|
||||
if isinstance(value, InstrumentedList):
|
||||
return [x.json for x in value]
|
||||
if type(value).__module__ != 'builtins': # Perhaps == builtin?
|
||||
return value.json
|
||||
except AttributeError:
|
||||
logger.debug(str(type(value)) + " was not converted to jsonifyable type")
|
||||
return None
|
||||
|
||||
return value
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
""" Define a base way to jsonify models
|
||||
Columns inside `to_json_filter` are excluded
|
||||
Columns inside `to_json_only_filter` are only included """
|
||||
return {
|
||||
column: RawBaseModel._to_json_types(value)
|
||||
# if not isinstance(value, datetime) else value.strftime('%Y-%m-%d')
|
||||
# if type(value).__module__ != self.__module__ # Perhaps == builtin?
|
||||
# else value.json # Convert instances to json if same module
|
||||
for column, value in self._to_dict().items()
|
||||
if column not in self.to_json_filter
|
||||
}
|
||||
|
||||
def _to_dict(self):
|
||||
""" This would more or less be the same as a `to_json`
|
||||
But putting it in a "private" function
|
||||
Allows to_json to be overriden without impacting __repr__
|
||||
Or the other way around
|
||||
And to add filter lists """
|
||||
return {
|
||||
self._map_columns(column.key): getattr(self, column.key)
|
||||
for column in inspect(self.__class__).attrs
|
||||
if not self.print_only or column.key in self.print_only
|
||||
}
|
||||
|
||||
def _map_columns(self, key):
|
||||
if key in self.column_map:
|
||||
return self.column_map[key]
|
||||
return key
|
||||
|
||||
def merge(self):
|
||||
db.raw_session.merge(self)
|
||||
return self
|
||||
|
||||
def commit(self):
|
||||
db.raw_session.commit()
|
||||
|
||||
def save(self):
|
||||
db.raw_session.add(self)
|
||||
db.raw_session.commit()
|
||||
return self
|
||||
|
||||
def delete(self):
|
||||
db.raw_session.delete(self)
|
||||
db.raw_session.commit()
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
pyjeeves.models
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Jeeves data models
|
||||
Jeeves meta data models
|
||||
"""
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
104
pyjeeves/models/raw.py
Normal file
104
pyjeeves/models/raw.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pyjeeves.models
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Jeeves raw data models
|
||||
"""
|
||||
# from sqlalchemy import Column, String
|
||||
from sqlalchemy.schema import MetaData, ForeignKey, Column
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.types import Integer, String
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
from sqlalchemy.sql.expression import and_
|
||||
|
||||
# from pyjeeves.session import raw_engine
|
||||
from . import db
|
||||
|
||||
from pyjeeves import logging
|
||||
from .abc import RawBaseModel
|
||||
|
||||
logger = logging.getLogger("PyJeeves." + __name__)
|
||||
logger.info("Reading Jeeves DB structure")
|
||||
|
||||
meta = MetaData()
|
||||
meta.reflect(bind=db.raw_session.connection(), only=['ar', 'ars', 'fr', 'kus', 'oh', 'lp', 'vg'])
|
||||
|
||||
# Table('fr', meta, implicit_returning=False)
|
||||
|
||||
Base = automap_base(cls=db.Model, name='Model', metadata=meta)
|
||||
|
||||
|
||||
class CommodityGroup(Base, RawBaseModel):
|
||||
__tablename__ = 'vg'
|
||||
column_map = {'VaruGruppKod': 'CommodityGroupNumber', 'VaruGruppBeskr': 'CommodityGroupName'}
|
||||
print_only = ('VaruGruppKod', 'VaruGruppBeskr')
|
||||
print_filter = ('Articles', 'articles_collection')
|
||||
# to_json_filter = ('Articles', 'articles_collection')
|
||||
|
||||
|
||||
class ArticleBalance(Base, RawBaseModel):
|
||||
__tablename__ = 'ars'
|
||||
column_map = {'LagSaldo': 'Balance',
|
||||
'LagResAnt': 'ReservedBalance',
|
||||
'LagsaldoAltEnh': 'BalanceAlternative',
|
||||
'LagResAntAltEnh': 'ReservedAlternativeBalance',
|
||||
'LagStalle': 'StorageLocationNumber'}
|
||||
print_only = ('LagSaldo',
|
||||
'LagResAnt',
|
||||
'LagsaldoAltEnh',
|
||||
'LagResAntAltEnh',
|
||||
'LagStalle')
|
||||
# print_filter = ('Articles', 'articles_collection')
|
||||
# to_json_filter = ('Articles', 'articles_collection')
|
||||
|
||||
ArtNr = Column(Integer, ForeignKey('ar.ArtNr'), primary_key=True)
|
||||
|
||||
|
||||
class Articles(Base, RawBaseModel):
|
||||
__tablename__ = 'ar'
|
||||
|
||||
column_map = {'ArtNr': 'ArticleNumber',
|
||||
'ArtBeskr': 'ArticleName',
|
||||
'LagSaldoArtikel': 'Balance',
|
||||
'EnhetsKod': 'Unit',
|
||||
'ArtListPris': 'ListPrice'}
|
||||
print_only = (
|
||||
'ArtNr',
|
||||
'ArtBeskr',
|
||||
'CommodityGroup',
|
||||
'ArticleBalance',
|
||||
'EnhetsKod',
|
||||
'LagSaldoArtikel',
|
||||
'RowCreatedDt',
|
||||
'ArtListPris')
|
||||
|
||||
ArtNr = Column(Integer, primary_key=True)
|
||||
|
||||
VaruGruppKod = Column(Integer, ForeignKey('vg.VaruGruppKod'), primary_key=True)
|
||||
|
||||
CommodityGroup = relationship(CommodityGroup)
|
||||
ArticleBalance = relationship(ArticleBalance)
|
||||
|
||||
@classmethod
|
||||
def _base_filters(self, obj):
|
||||
return RawBaseModel._base_filters(
|
||||
obj,
|
||||
and_(obj.LagTyp == 0)
|
||||
)
|
||||
|
||||
|
||||
class Companies(Base, RawBaseModel):
|
||||
__tablename__ = 'fr'
|
||||
column_map = {'FtgNr': 'CompanyNumber', 'FtgNamn': 'CompanyName'}
|
||||
print_only = ('CompanyNumber', 'CompanyName')
|
||||
|
||||
FtgNr = Column(String, primary_key=True)
|
||||
|
||||
|
||||
Base.prepare()
|
||||
|
||||
# Base companies for cusomters and suppliers
|
||||
Customers = Base.classes.kus # Customer information
|
||||
Orders = Base.classes.oh # Orders by customers
|
||||
DelivLoc = Base.classes.lp # Connections between a delivery company and customer company
|
||||
Loading…
Add table
Add a link
Reference in a new issue