Beispiel #1
0
def get_edge_dst(edge, allow_query=False):
    """Return the edge's destination or None.

    """

    if edge.dst:
        dst = edge.dst
    elif edge.dst_id is not None and allow_query:
        dst_class = Node.get_subclass_named(edge.__dst_class__)
        dst = (edge.get_session().query(dst_class).filter(
            dst_class.node_id == edge.dst_id).first())
    else:
        dst = None

    return dst
Beispiel #2
0
def get_edge_src(edge):
    """Return the edge's source or None.

    Attempts to lookup the node by id if the association proxy is not
    set.

    """

    if edge.src:
        src = edge.src
    elif edge.src_id is not None:
        src_class = Node.get_subclass_named(edge.__src_class__)
        src = (edge.get_session().query(src_class).filter(
            src_class.node_id == edge.src_id).first())
    else:
        src = None
    return src
Beispiel #3
0
    def convert_link_value(to_cls, link_name, prop, value):
        """
        Cast value based on key.
        Args:
            to_cls: class to be converted
            link_name: name of link to be converted
            key: property is converted
            value: value is concerted

        Return:
            value with correct type
        """
        if value is None or value in ["", "null"]:
            return None

        link_name, value = strip(link_name), strip(value)
        edge = getattr(to_cls, link_name)
        return DelimitedConverter.get_converted_type_from_list(
            Node.get_subclass_named(edge.target_class.__dst_class__), prop,
            value)
Beispiel #4
0
#!/usr/bin/env python

from psqlgraph import Node, Edge
from gdcdatamodel import models as md

CACHE_EDGES = {
    Node.get_subclass_named(edge.__src_class__): edge
    for edge in Edge.get_subclasses() if 'RelatesToCase' in edge.__name__
}

LEVEL_1_SQL = """

INSERT INTO {cache_edge_table} (src_id, dst_id, _props, _sysan, acl)
SELECT {cls_table}.node_id, node_case.node_id,
       '{{}}'::jsonb, '{{}}'::jsonb, '{{}}'::text[]
    FROM {cls_table}

    -- Step directly to case
    JOIN {cls_to_case_edge_table}
         ON {cls_table}.node_id = {cls_to_case_edge_table}.src_id
    JOIN node_case
         ON node_case.node_id = {cls_to_case_edge_table}.dst_id

    -- Append only, e.g. insert only those missing
    WHERE NOT EXISTS (
          SELECT 1 FROM {cache_edge_table}
          WHERE {cls_table}.node_id = {cache_edge_table}.src_id
          AND   node_case.node_id   = {cache_edge_table}.dst_id)
"""

APPEND_CACHE_FROM_PARENT_SQL = """
Beispiel #5
0
def related_cases_from_parents(node):
    """Get the cached related case ids from the parents of this node from

    1. The shortcut edges of any parent nodes
    2. If any parents are cases, include those ids

    :param node: The Node instance
    :returns: List of Case nodes

    """

    skip_edges_named = [get_related_case_edge_cls_name(node)]

    # Make sure the edges haven't been expunged
    edges_out = [e for e in node.edges_out if e in node.get_session()]

    # Get the cached ids from parents
    cases = {
        case
        for edge in edges_out if edge.dst
        for case in edge.dst._related_cases_from_cache
        if edge.__class__.__name__ not in skip_edges_named
    }

    # Are any parents cases?
    for edge in edges_out:
        if edge.__class__.__name__ in skip_edges_named:
            continue
        dst_class = Node.get_subclass_named(edge.__dst_class__)
        if dst_class.label == 'case' and edge.dst:
            cases.add(edge.dst)

    return list(filter(None, cases))


def cache_related_cases_recursive(node,
                                  session,
                                  flush_context,
                                  instances,
                                  visited_nodes=None):
    """Update the related case cache on source node and its children
    recursively iff the this update changes the related case source
    node's shortcut edges.

    :param session: The target Session.
    :param flush_context:
        Internal UOWTransaction object which handles the details of
        the flush.
    :param instances:
        Usually None, this is the collection of objects which can be
        passed to the Session.flush() method (note this usage is
        deprecated).

    """

    visited_nodes = set() if visited_nodes is None else visited_nodes

    # Check preconditions for updating shortcut edge
    if not node:
        return

    if not hasattr(node, RELATED_CASES_LINK_NAME):
        return

    if visited_nodes and node.node_id in visited_nodes:
        return

    visited_nodes.add(node.node_id)

    # These are the cases that are currently connected by a shortcut edge
    current_cases = {c.node_id: c for c in related_cases_from_cache(node)}

    # These are the cases are currently connected by a shortcut edge
    # to this node's parents
    updated_cases = {c.node_id: c for c in related_cases_from_parents(node)}

    current_case_ids = set(current_cases.keys())
    updated_case_ids = set(updated_cases.keys())
    diff = current_case_ids.symmetric_difference(updated_case_ids)

    # If nothing has changed, we don't need to update or recur
    if not diff:
        return

    update_cache_edges(node, session, updated_cases)

    to_recur = [e for e in node.edges_in if e.src]
    for edge in to_recur:
        cache_related_cases_recursive(
            get_edge_src(edge),
            session,
            flush_context,
            instances,
            visited_nodes,
        )

    return


def update_cache_edges(node, session, correct_cases):
    """Creates new edges or deletes old edges"""

    assoc_proxy = getattr(node, RELATED_CASES_LINK_NAME)

    # Get information about the existing edges
    edge_name = get_related_case_edge_cls_name(node)
    existing_edges = getattr(node, '_{}_out'.format(edge_name))

    # Remove edges that should no longer exist
    cases_disconnected = [
        edge.dst for edge in existing_edges if edge.dst_id not in correct_cases
    ]

    for case in cases_disconnected:
        assoc_proxy.remove(case)

    existing_edge_dst_case_ids = {edge.dst_id for edge in existing_edges}

    cases_connected = [
        case for case_id, case in correct_cases.items()
        if case_id not in existing_edge_dst_case_ids
    ]

    for case in cases_connected:
        assoc_proxy.append(case)


def cache_related_cases_on_insert(target, session, flush_context, instances):
    """Hook on updated edges.  Update the related case cache on source
    node and its children iff the this update changes the related case
    source node's cache.

    This will be called when a node association proxy instance is
    removed.

    :param session: The target Session.
    :param flush_context:
        Internal UOWTransaction object which handles the details of
        the flush.
    :param instances:
        Usually None, this is the collection of objects which can be
        passed to the Session.flush() method (note this usage is
        deprecated).

    """

    if not target.src:
        target.src = get_edge_src(target)

    if not target.dst:
        target.dst = get_edge_dst(target, allow_query=True)

    cache_related_cases_recursive(
        get_edge_src(target),
        session,
        flush_context,
        instances,
    )


def cache_related_cases_on_update(target, session, flush_context, instances):
    """Hook on deleted edges.  Update the related case cache on source
    node and its children.

    This will be called when an edge instance is updated
    (i.e. association proxy is deleted).

    :param session: The target Session.
    :param flush_context:
        Internal UOWTransaction object which handles the details of
        the flush.
    :param instances:
        Usually None, this is the collection of objects which can be
        passed to the Session.flush() method (note this usage is
        deprecated).

    """
    cache_related_cases_recursive(
        get_edge_src(target),
        session,
        flush_context,
        instances,
    )


def cache_related_cases_on_delete(target, session, flush_context, instances):
    """Hook on deleted edges.  Update the related case cache on source
    node and its children.

    This will be called when an edge instance is explicitly deleted in
    the session.

    :param session: The target Session.
    :param flush_context:
        Internal UOWTransaction object which handles the details of
        the flush.
    :param instances:
        Usually None, this is the collection of objects which can be
        passed to the Session.flush() method (note this usage is
        deprecated).

    """
    # Remove the source and destination of application local
    # association_proxy so cache_related_cases_update_children doesn't
    # traverse the edge
    target.dst, target.src = None, None
    cache_related_cases_recursive(
        get_edge_src(target),
        session,
        flush_context,
        instances,
    )