def inflate_results(results, resources_to_expand): """ Inflate and serialize the results. :param str results: results obtained from Neo4j :param list resources_to_expand: resources to expand :return: a list of dictionaries containing serialized results received from Neo4j :rtype: list """ results_list = [] for raw_result in results: temp = {} for node in raw_result: if node: inflated_node = inflate_node(node) node_label = inflated_node.__label__ if node_label not in temp: temp[node_label] = [] if node_label.lower() in resources_to_expand: serialized_node = inflated_node.serialized_all else: serialized_node = inflated_node.serialized serialized_node['resource_type'] = node_label if serialized_node not in temp[node_label]: temp[node_label].append(serialized_node) results_list.append(temp) return results_list
def inflate_results(results): """ Inflate the results. :param str results: results obtained from Neo4j :return: a list of dictionaries containing serialized results received from Neo4j :rtype: list """ results_list = [] for raw_result in results: nodes = [] for node in raw_result: if node: inflated_node = inflate_node(node) nodes.append(inflated_node) results_list.append(nodes) return results_list
def get_recent_nodes(): """ Get the most recent nodes of each node type. :return: a tuple with the first value as a dictionary with the keys as names of each node type, and values of arrays of the most recents of each node, and the second as metadata :rtype: tuple """ timestamp_dict = { FreshmakerEvent.__label__: 'time_created', BugzillaBug.__label__: 'modified_time', DistGitCommit.__label__: 'commit_date', KojiBuild.__label__: 'completion_time', Advisory.__label__: 'update_date' } id_dict = {} final_result_data = {} final_result_metadata = { 'id_keys': id_dict, 'timestamp_keys': timestamp_dict } for label, time_property in timestamp_dict.items(): query = ( 'MATCH (node:{label}) ' 'WHERE node.{time_property} IS NOT NULL ' 'RETURN node ' 'ORDER BY node.{time_property} DESC LIMIT 5' ).format(label=label, time_property=time_property) results, _ = db.cypher_query(query) for result in results: node_results = final_result_data.setdefault(label, []) # result is always a list of a single node node = inflate_node(result[0]) serialized_node = node.serialized_all serialized_node['resource_type'] = node.__label__ serialized_node['display_name'] = node.display_name node_results.append(serialized_node) if node.__label__ not in id_dict: id_dict[node.__label__] = node.unique_id_property return (final_result_data, final_result_metadata)
def serialized_all(self): """ Generate a serialized form of the node that includes all its relationships. :return: a serialized form of the node with relationships :rtype: dictionary :raises RuntimeError: if the label of a Neo4j node can't be mapped back to a neomodel class """ # Avoid circular imports from estuary.models import models_inheritance # A set that will keep track of all properties on the node that weren't returned from Neo4j null_properties = set() # A mapping of Neo4j relationship names in the format of: # { # node_label: { # relationship_name: {direction: (property_name, cardinality_class) ...}, # } # } relationship_map = {} for property_name, relationship in self.__all_relationships__: node_label = relationship.definition['node_class'].__label__ relationship_name = relationship.definition['relation_type'] for label in models_inheritance[node_label]: if label not in relationship_map: relationship_map[label] = {} relationship_direction = relationship.definition['direction'] if relationship_direction == EITHER: # The direction can be coming from either direction, so map both properties = { INCOMING: (property_name, relationship.manager), OUTGOING: (property_name, relationship.manager), } else: properties = { relationship_direction: (property_name, relationship.manager) } if relationship_name not in relationship_map[label]: relationship_map[label][relationship_name] = properties else: relationship_map[label][relationship_name].update( properties) null_properties.add(property_name) # This variable will contain the current node as serialized + all relationships serialized = self.serialized # Get all the direct relationships in both directions results, _ = self.cypher( 'MATCH (a) WHERE id(a)={self} MATCH (a)-[r]-(all) RETURN r, all') for relationship, node in results: # If the starting node in the relationship is the same as the node being serialized, # we know that the relationship is outgoing if relationship.start_node.id == self.id: direction = OUTGOING else: direction = INCOMING # Convert the Neo4j result into a model object inflated_node = inflate_node(node) try: property_name, cardinality_class = \ relationship_map[inflated_node.__label__][relationship.type][direction] except KeyError: if direction == OUTGOING: direction_text = 'outgoing' else: direction_text = 'incoming' log.warn( 'An {0} {1} relationship of {2!r} with {3!r} is not mapped in the models and ' 'will be ignored'.format(direction_text, relationship.type, self, inflate_node)) continue if not serialized.get(property_name): null_properties.remove(property_name) if cardinality_class in (One, ZeroOrOne): serialized[property_name] = inflated_node.serialized else: if not serialized.get(property_name): serialized[property_name] = [] serialized[property_name].append(inflated_node.serialized) # Neo4j won't return back relationships it doesn't know about, so just make them empty # so that the keys are always consistent for property_name in null_properties: prop = getattr(self, property_name) if isinstance(prop, One) or isinstance(prop, ZeroOrOne): serialized[property_name] = None else: serialized[property_name] = [] return serialized
def get_siblings(resource, uid): """ Get siblings of next/previous node that are correlated to the node in question. :param str resource: a resource name that maps to a neomodel class :param str uid: the value of the UniqueIdProperty to query with :return: a Flask JSON response :rtype: flask.Response :raises NotFound: if the item is not found :raises ValidationError: if an invalid resource was requested """ story_type_mapper = { 'container': 'ContainerStoryManager', 'module': 'ModuleStoryManager' } story_type = request.args.get('story_type', 'container').lower() story_manager_class = story_type_mapper.get(story_type) if story_manager_class: story_manager = getattr(estuary.utils.story, story_manager_class)() else: raise ValidationError( 'Supplied story type is invalid. Select from: {0}'.format( ', '.join(current_app.config['STORY_MANAGER_SEQUENCE']))) # This is the node that is part of a story which has the relationship to the desired # sibling nodes story_node = get_neo4j_node(resource, uid) if not story_node: raise NotFound('This item does not exist') story_node_story_flow = story_manager.story_flow(story_node.__label__) # If backward_rel is true, we fetch siblings of the previous node, next node otherwise. # For example, if you pass in an advisory and want to know the Freshmaker events triggered from # that advisory, backward_rel would be false. If you want to know the Koji builds attached to # that advisory, then backward_rel would true. backward = str_to_bool(request.args.get('backward_rel')) if backward and story_node_story_flow['backward_label']: desired_siblings_label = story_node_story_flow['backward_label'] elif not backward and story_node_story_flow['forward_label']: desired_siblings_label = story_node_story_flow['forward_label'] else: raise ValidationError( 'Siblings cannot be determined on this kind of resource') sibling_nodes = story_manager.get_sibling_nodes(desired_siblings_label, story_node) # Inflating and formatting results from Neo4j serialized_results = [] for result in sibling_nodes: inflated_node = inflate_node(result[0]) serialized_node = inflated_node.serialized_all serialized_node['resource_type'] = inflated_node.__label__ serialized_node['display_name'] = inflated_node.display_name serialized_results.append(serialized_node) description = story_manager.get_siblings_description( story_node.display_name, story_node_story_flow, backward) result = {'data': serialized_results, 'meta': {'description': description}} return jsonify(result)