예제 #1
0
    def __init__(self, derive: InstrumentedAttribute, from_parent: any):
        super(Copy, self).__init__(derive)
        if isinstance(from_parent, str):
            names = from_parent.split('.')
            self._from_parent_role = names[0]
            self._from_column = names[1]
        elif isinstance(from_parent, InstrumentedAttribute):
            self._from_column = from_parent.key
            table_class = from_parent.class_
            parent_class_name = self.get_class_name(table_class)
            pass
            attrs = self._derive.parent.attrs
            found_attr = None
            for each_attr in attrs:
                if isinstance(each_attr, RelationshipProperty):
                    each_parent_class_nodal_name = each_attr.entity.class_
                    each_parent_class_name = self.get_class_name(
                        each_parent_class_nodal_name)
                    if each_parent_class_name == parent_class_name:
                        if found_attr is not None:
                            raise Exception(
                                "TODO / copy - disambiguate relationship")
                        found_attr = each_attr
            if found_attr is None:
                raise Exception("Invalid 'as_sum_of' - not a reference to: " +
                                self.table + " in " + self.__str__())
            else:
                self._from_parent_role = found_attr.key

        else:
            pass
        rb = RuleBank()
        rb.deposit_rule(self)
예제 #2
0
 def __init__(self, derive: str, as_sum_of: str, where: str):
     super(Sum, self).__init__(derive)
     self._as_sum_of = as_sum_of  # could probably super-ize parent accessor
     self._from_parent_role = self._as_sum_of.split(".")[0]
     self._where = where
     rb = RuleBank()
     rb.deposit_rule(self)
예제 #3
0
 def __init__(self, derive: str, as_count_of: str, where: str):
     super(Count, self).__init__(derive)
     self._as_count_of = as_count_of  # could probably super-ize parent accessor
     self._from_parent_role = as_count_of
     self._where = where
     rb = RuleBank()
     rb.deposit_rule(self)
예제 #4
0
 def __init__(self, derive: str, from_parent: str):
     super(Copy, self).__init__(derive)
     names = from_parent.split('.')
     self._from_parent_role = names[0]
     self._from_column = names[1]
     rb = RuleBank()
     rb.deposit_rule(self)
예제 #5
0
def validate(a_session: session, engine: Engine):
    """
    Determine formula execution order based on "row.xx" references,
    (or raise exception if cycles detected).
    """
    list_rules = "\n\nValidate Rule Bank"
    rules_bank = RuleBank()

    for each_key in rules_bank._tables:
        validate_formula_dependencies(class_name=each_key)
    list_rules += rules_bank.__str__()
    print(list_rules)
    return True
예제 #6
0
    def update_referenced_parent_attributes(self, dependencies: list):
        """
        Used by Formulas and constraints log their dependence on parent attributes
        This sets RuleBank.TableRules[mapped_class].referring_children
        dependencies is a list

        But, can't do this now, because meta_contains_role_name = False
        So, do it on the fly in logic_row (which is an ugh)
        """
        meta_contains_role_name = False
        if meta_contains_role_name is False:
            return
        else:
            meta_data = rule_bank_withdraw.get_meta_data()
            child_meta = meta_data.tables[self.table]
            parent_role_name = dependencies[1]
            foreign_keys = child_meta.foreign_keys
            for each_foreign_key in foreign_keys:  # eg, OrderDetail has OrderHeader, Product
                each_parent_class_name = each_foreign_key.name
                each_parent_role_name = each_foreign_key.key
                if parent_role_name == each_parent_role_name:  # eg, OrderHeader
                    rule_bank = RuleBank()
                    if each_parent_class_name not in rule_bank._tables:
                        self._tables[rule_bank] = TableRules()
                    table_rules = self._tables[rule_bank]
                    if table_rules.referring_children is None:
                        table_rules.referring_children = {}
                    if parent_role_name not in table_rules.referring_children:
                        table_rules.referring_children[parent_role_name] = []
                    table_rules.referring_children.append(dependencies[2])
                    engine_logger.debug(
                        prt("child parent dependency: " + dependencies[1]))
                    break
예제 #7
0
    def __init__(self, row: base, old_row: base, ins_upd_dlt: str,
                 nest_level: int, a_session: session, row_cache: object):
        self.session = a_session
        self.row = row  # type(base)
        """ mapped row """
        self.old_row = old_row
        """ old mapped row """
        self.ins_upd_dlt = ins_upd_dlt
        self.ins_upd_dlt_initial = ins_upd_dlt  # order inserted, then adjusted
        self.nest_level = nest_level
        self.reason = "?"  # set by insert, update and delete
        """ if starts with cascade, triggers cascade processing """

        self.row_cache = row_cache
        if row_cache is not None:  # eg, for debug as in upd_order_shipped test
            row_cache.add(logic_row=self)

        rb = RuleBank()
        self.rb = rb
        self.session = rb._session
        self.engine = rb._engine
        self.some_base = declarative_base()

        self.name = type(self.row).__name__
        self.table_meta = row.metadata.tables[type(self.row).__name__]
        self.inspector = Inspector.from_engine(self.engine)
예제 #8
0
def aggregate_rules(child_logic_row: LogicRow) -> dict:
    """returns dict(<parent_role_name>, sum/count_rules[] for given child_table_name

    This requires we **invert** the RuleBank,
      to find sums that reference child_table_name, grouped by parent_role
    e.g., for child_logic_row "Order", we return
      ["Order", (Customer.balance, Customer.order_count...)
      ["Employee, (Employee.order_count)]
    """
    result_role_rules_list = {}  # dict of RoleRules

    child_mapper = object_mapper(child_logic_row.row)
    rule_bank = RuleBank()
    relationships = child_mapper.relationships
    for each_relationship in relationships:  # eg, order has parents cust & emp, child orderdetail
        if each_relationship.direction == sqlalchemy.orm.interfaces.MANYTOONE:  # cust, emp
            child_role_name = each_relationship.back_populates  # eg, OrderList
            if child_role_name is None:
                child_role_name = child_mapper.class_.__name__  # default TODO design review
            parent_role_name = each_relationship.key  # eg, Customer TODO design review
            parent_class_name = each_relationship.entity.class_.__name__
            if parent_class_name in rule_bank._tables:
                parent_rules = rule_bank._tables[parent_class_name].rules
                for each_parent_rule in parent_rules:  # (..  bal = sum(OrderList.amount) )
                    if isinstance(each_parent_rule, (Sum, Count)):
                        if each_parent_rule._child_role_name == child_role_name:
                            if parent_role_name not in result_role_rules_list:
                                result_role_rules_list[parent_role_name] = []
                            result_role_rules_list[parent_role_name].append(
                                each_parent_rule)
                            each_parent_rule._parent_role_name = parent_role_name
    return result_role_rules_list
예제 #9
0
def get_formula_rules(class_name: str) -> list:
    """withdraw rules of designated a_class
    """
    rule_bank = RuleBank()
    rules_list = []
    role_rules_list = {}  # dict of RoleRules
    for each_rule in rule_bank._tables[class_name].rules:
        if isinstance(each_rule, Formula):
            rules_list.append(each_rule)
    return rules_list
예제 #10
0
def rules_of_class(a_table_name: str, a_class: (Formula, Constraint)) -> list:
    """withdraw rules of designated a_class
    """
    rule_bank = RuleBank()
    rules_list = []
    role_rules_list = {}  # dict of RoleRules
    for each_rule in rule_bank._tables[a_table_name]:
        if isinstance(each_rule, a_class):
            rules_list.append(each_rule)
    return rules_list
예제 #11
0
 def __init__(self, derive: InstrumentedAttribute, as_sum_of: any,
              where: any):
     super(Sum, self).__init__(derive)
     self._as_sum_of = as_sum_of  # could probably super-ize parent accessor
     self._where = where
     if isinstance(as_sum_of, str):
         self._child_role_name = self._as_sum_of.split(".")[
             0]  # child role retrieves children
         self._child_summed_field = self._as_sum_of.split(".")[1]
     elif isinstance(as_sum_of, InstrumentedAttribute):
         self._child_summed_field = as_sum_of.key
         attrs = as_sum_of.parent.attrs
         found_attr = None
         for each_attr in attrs:
             if isinstance(each_attr, RelationshipProperty):
                 pass
                 parent_class_nodal_name = each_attr.entity.class_
                 parent_class_name = self.get_class_name(
                     parent_class_nodal_name)
                 if parent_class_name == self.table:
                     if found_attr is not None:
                         raise Exception("TODO - disambiguate relationship")
                     found_attr = each_attr
         if found_attr is None:
             raise Exception("Invalid 'as_sum_of' - not a reference to: " +
                             self.table + " in " + self.__str__())
         else:
             self._child_role_name = found_attr.back_populates
     else:
         raise Exception(
             "as_sum_of must be either string, or <mapped-class.column>: " +
             str(as_sum_of))
     if where is None:
         self._where_cond = lambda row: True
     elif isinstance(where, str):
         self._where_cond = lambda row: eval(where)
     elif isinstance(where, Callable):
         self._where_cond = where
     else:
         raise Exception("'where' must be string, or lambda: " +
                         self.__str__())
     rb = RuleBank()
     rb.deposit_rule(self)
예제 #12
0
def copy_rules(a_table_name: str) -> CopyRulesForTable:
    """dict(<role_name>, copy_rules[]
    """
    rule_bank = RuleBank()
    role_rules_list = {}  # dict of RoleRules
    for each_rule in rule_bank._tables[a_table_name]:
        if isinstance(each_rule, Copy):
            role_name = each_rule._from_parent_role
            if role_name not in role_rules_list:
                role_rules_list[role_name] = []
            role_rules_list[role_name].append(each_rule)
    return role_rules_list
예제 #13
0
def aggregate_rules(a_table_name: str) -> dict:
    """dict(<role_name>, sum/count_rules[]
    """
    rule_bank = RuleBank()
    role_rules_list = {}  # dict of RoleRules
    for each_rule in rule_bank._tables[a_table_name]:
        if isinstance(each_rule, (Sum, Count)):
            role_name = each_rule._from_parent_role
            if role_name not in role_rules_list:
                role_rules_list[role_name] = []
            role_rules_list[role_name].append(each_rule)
    return role_rules_list
예제 #14
0
def generic_rules_of_class(a_class: (Formula, Constraint,
                                     EarlyRowEvent)) -> list:
    """withdraw rules of the "*" (any) class
    """
    rule_bank = RuleBank()
    rules_list = []
    role_rules_list = {}  # dict of RoleRules
    if "*" in rule_bank._tables:
        for each_rule in rule_bank._tables["*"].rules:
            if isinstance(each_rule, a_class):
                rules_list.append(each_rule)
    return rules_list
예제 #15
0
def setup(a_session: session, an_engine: Engine):
    rules_bank = RuleBank()
    rules_bank._session = a_session
    event.listen(a_session, "before_flush", before_flush)
    rules_bank._tables = {}
    rules_bank._at = datetime.now()

    rules_bank._engine = an_engine
    rules_bank._rb_base = declarative_base  # FIXME good grief, not appearing, no error
    return
예제 #16
0
def rules_of_class(
    logic_row: LogicRow,
    a_class: (Formula, Constraint, EarlyRowEvent)) -> list:
    """withdraw rules of designated a_class
    """
    rule_bank = RuleBank()
    rules_list = []
    role_rules_list = {}  # dict of RoleRules
    if logic_row.name in rule_bank._tables:
        for each_rule in rule_bank._tables[logic_row.name].rules:
            if isinstance(each_rule, a_class):
                rules_list.append(each_rule)
    return rules_list
예제 #17
0
 def __init__(
         self,
         validate: object,
         error_msg: str,
         calling: Callable = None,
         as_condition: object = None):  # str or lambda boolean expression
     super(Constraint, self).__init__(validate)
     # self.table = validate  # setter finds object
     self._error_msg = error_msg
     self._as_condition = as_condition
     self._calling = calling
     if calling is None and as_condition is None:
         raise Exception(
             f'Constraint {str} requires calling or as_expression')
     if calling is not None and as_condition is not None:
         raise Exception(
             f'Constraint {str} either calling or as_expression')
     if calling is not None:
         self._function = calling
     elif isinstance(as_condition, str):
         self._as_condition = lambda row: eval(as_condition)
     ll = RuleBank()
     ll.deposit_rule(self)
예제 #18
0
    def __init__(self, derive: InstrumentedAttribute,
                 as_exp: str = None,              # for very short expressions
                 as_expression: Callable = None,  # short, with type checking
                 calling: Callable = None         # complex formula
                 ):
        """
        Specify rep
          * as_exp - string (for very short expressions - price * quantity)
          * ex_expression - lambda (for type checking)
          * calling - function (for more complex formula, with old_row)

        """
        super(Formula, self).__init__(derive)

        self._as_exp = as_exp
        self._as_expression = as_expression
        self._function = calling

        self._as_exp_lambda = None   # we exec this, or _function

        valid_count = 0
        if as_exp is not None:
            self._as_exp_lambda = lambda row: eval(as_exp)
            valid_count += 1
        if as_expression is not None:
            self._as_exp_lambda = as_expression
            valid_count += 1
        if calling is not None:
            valid_count += 1
        if valid_count != 1:
            raise Exception(f'Formula requires one of as_exp, as_expression or calling')
        self._dependencies = []
        text = self.get_rule_text()
        self.parse_dependencies(rule_text=text)
        self._exec_order = -1  # will be computed in rule_bank_setup (all rules loaded)
        rb = RuleBank()
        rb.deposit_rule(self)
예제 #19
0
    def __init__(self, row: base, old_row: base, ins_upd_dlt: str,
                 nest_level: int):
        self.row = row
        self.old_row = old_row
        self.ins_upd_dlt = ins_upd_dlt
        self.nest_level = nest_level

        rb = RuleBank()
        self.rb = rb
        self.session = rb._session
        self.engine = rb._engine
        self.some_base = declarative_base()

        self.name = type(self.row).__name__
        self.table_meta = row.metadata.tables[type(self.row).__name__]
        self.inspector = Inspector.from_engine(self.engine)
예제 #20
0
def setup(a_session: session, an_engine: Engine
          ):  # TODO major - ensure compatible with fab, flask etc
    """
    Initialize the RuleBank

    """
    rules_bank = RuleBank()
    rules_bank._session = a_session
    event.listen(a_session, "before_flush", before_flush)

    rules_bank._tables = {}
    rules_bank._at = datetime.now()

    rules_bank._engine = an_engine
    rules_bank._metadata = MetaData(bind=an_engine, reflect=True)
    from sqlalchemy.ext.declarative import declarative_base
    rules_bank._base = declarative_base()

    return
예제 #21
0
def get_referring_children(parent_logic_row: LogicRow) -> dict:
    """
    return RulesBank[class_name].referring_children (create if None)
    referring_children is <parent_role_name>, parent_attribute_list()
    """
    rule_bank = RuleBank()
    table_rules = rule_bank._tables[parent_logic_row.name]
    result = table_rules.referring_children
    if result is not None:
        return result
    else:
        # sigh, best to have build this in rule_bank_setup, but unable to get mapper
        # FIXME design is this threadsafe?
        table_rules.referring_children = {}
        parent_mapper = object_mapper(parent_logic_row.row)
        parent_relationships = parent_mapper.relationships
        for each_parent_relationship in parent_relationships:  # eg, order has parents cust & emp, child orderdetail
            if each_parent_relationship.direction == sqlalchemy.orm.interfaces.ONETOMANY:  # cust, emp
                parent_role_name = each_parent_relationship.back_populates  # eg, OrderList
                table_rules.referring_children[parent_role_name] = []
                child_role_name = each_parent_relationship.key
                child_class_name = get_child_class_name(
                    each_parent_relationship)  # eg, OrderDetail
                child_table_rules = rule_bank._tables[child_class_name].rules
                search_for_rew_parent = "row." + parent_role_name
                if child_table_rules is not None:
                    for each_rule in child_table_rules:
                        if isinstance(
                                each_rule,
                            (Formula,
                             Constraint)):  # eg, OrderDetail.ShippedDate
                            rule_text = each_rule.get_rule_text(
                            )  #        eg, row.OrderHeader.ShippedDate
                            rule_words = rule_text.split()
                            for each_word in rule_words:
                                if each_word.startswith(search_for_rew_parent):
                                    rule_terms = each_word.split(".")
                                    # if parent_role_name not in table_rules.referring_children:
                                    #    table_rules.referring_children[parent_role_name] = ()
                                    table_rules.referring_children[
                                        parent_role_name].append(rule_terms[2])
        return table_rules.referring_children
예제 #22
0
 def __init__(self, validate: str, calling):
     super(Constraint, self).__init__(validate)
     # self.table = validate  # setter finds object
     self._function = calling
     ll = RuleBank()
     ll.deposit_rule(self)
예제 #23
0
 def __init__(self, derive: str, calling: Callable):
     super(Formula, self).__init__(derive)
     self._function = calling
     rb = RuleBank()
     rb.deposit_rule(self)
예제 #24
0
def get_meta_data():
    rule_bank = RuleBank()
    return rule_bank._metadata
예제 #25
0
def get_session():
    rule_bank = RuleBank()
    return rule_bank._session
예제 #26
0
 def __init__(self, on_class: object, calling: Callable = None):
     super(AbstractRowEvent, self).__init__(on_class)
     self._function = calling
     ll = RuleBank()
     ll.deposit_rule(self)
예제 #27
0
basedir = os.path.dirname(basedir)
conn_string = "sqlite:///" + os.path.join(basedir, "nw-app/nw.db")
# e.g. 'sqlite:////Users/val/python/vsc/logic-explore/nw-app/nw.db'
engine = sqlalchemy.create_engine(conn_string,
                                  echo=False)  # sqlalchemy sqls...

# Create a session
session_maker = sqlalchemy.orm.sessionmaker()
session_maker.configure(bind=engine)
session = session_maker()

do_logic = True
rule_list = None
db = None
if do_logic:
    rule_bank = RuleBank()
    rule_bank_setup.setup(session, engine)
    from .nw_rules_bank import NwLogic
    rule_bank = RuleBank(
    )  # FIXME - unclear why this returns the *correct* singleton, vs 2 lines above
    copies = rule_bank_withdraw.copy_rules("OrderDetail")
    aggregates = rule_bank_withdraw.aggregate_rules("Customer")
    constraints = rule_bank_withdraw.rules_of_class("Customer", Constraint)
    formulas = rule_bank_withdraw.rules_of_class("OrderDetail", Formula)
    print("\n\nlogic loaded:\n" + str(rule_bank))

# target, modifier, function
event.listen(session, "before_commit", nw_before_commit)
event.listen(session, "before_flush", nw_before_flush)

# event.listen(Order.ShippedDate, "set", order_modified)