def create_collections_categories_graph(db: StandardDatabase) -> None: if db.has_graph(GRAPH_COLLECTIONS_CATEGORIES): return graph = db.create_graph(GRAPH_COLLECTIONS_CATEGORIES) # Language -> Collections graph.create_edge_definition( edge_collection=EDGE_COLLECTION_LANGUAGE_HAS_COLLECTIONS, from_vertex_collections=[COLLECTION_LANGUAGES], to_vertex_collections=[COLLECTION_MENU_COLLECTIONS], ) # Collection -> Categories graph.create_edge_definition( edge_collection=EDGE_COLLECTION_COLLECTION_HAS_CATEGORIES, from_vertex_collections=[COLLECTION_MENU_COLLECTIONS], to_vertex_collections=[COLLECTION_MENU_CATEGORIES], ) # Category -> Files graph.create_edge_definition( edge_collection=EDGE_COLLECTION_CATEGORY_HAS_FILES, from_vertex_collections=[COLLECTION_MENU_CATEGORIES], to_vertex_collections=[COLLECTION_FILES], )
def create(db: StandardDatabase, game: Game, creator=None): """ save a game to the database :param db: connection :param game: target :param creator: creator of the game """ logger.info(f'create called for {game.title}') if not db.has_collection('games'): logger.info(f'games collection does not exist, creating it.') db.create_collection('games') db_games = db.collection('games') if db_games.find({'key': game.title}): logger.warning(f'{game.title} already in metadata') raise GameStateException(f'{game.title} already in metadata') if db.has_graph(game.title): logger.warning(f'{game.title} already defined') raise GameStateException(f'{game.title} already defined') if not nx.is_directed_acyclic_graph(game.graph): logger.warning(f'{game.title} is not acyclic') raise GameStateException(f'{game.title} is not acyclic') if not game.start_is_set(): logger.warning(f'{game.title} has no starting point') raise GameStateException(f'{game.title} has no starting point') db_game_graph = db.create_graph(f'game_{game.title}') if not db_game_graph.has_vertex_collection('waypoints'): logger.info(f'waypoints vertex collection does not exist, creating it.') db_game_graph.create_vertex_collection('waypoints') path = db_game_graph.create_edge_definition( edge_collection='path', from_vertex_collections=['waypoints'], to_vertex_collections=['waypoints'] ) # add all game nodes for waypoint_vertex in set(game.start.all_path_nodes()): logger.debug(f'inserting waypoint vertex {repr(waypoint_vertex)}') db_game_graph.insert_vertex('waypoints', json.dumps(waypoint_vertex, cls=MarugotoEncoder)) # tasks as documents if not db.has_collection('tasks'): logger.info(f'tasks collection does not exist, creating it.') db.create_collection('tasks') tasks = db.collection('tasks') # dialogs metadata if not db.has_collection('dialogs'): logger.info(f'dialogs does not exist, creating it.') db.create_collection('dialogs') db_dialogs = db.collection('dialogs') # npc dialog metadata if not db.has_collection('npcs'): logger.info(f'npcs collection does not exist, creating it.') db.create_collection('npcs') db_npcs = db.collection('npcs') for npc in game.npcs: if db_npcs.find({'_key': f'{game.title}-{npc.first_name}-{npc.last_name}'}): logger.warning(f'dialog {game.title}-{npc.first_name}-{npc.last_name} already in metadata') raise GameStateException(f'dialog {game.title}-{npc.first_name}-{npc.last_name} already in metadata') logger.debug(f'inserting npc {game.title}-{npc.first_name}-{npc.last_name} with dialog {npc.dialog.id.hex}') db_npcs.insert({ '_key': f'{game.title}-{npc.first_name}-{npc.last_name}', 'game': f'game_{game.title}', 'first_name': npc.first_name, 'last_name': npc.last_name, 'salutation': npc.salutation, 'mail': npc.mail, 'image': base64.encodebytes(npc.image) if npc.image else None, 'dialog': npc.dialog.id.hex }) if db_dialogs.find({'_key': npc.dialog.id.hex}): logger.warning(f'dialog {npc.dialog.id} already in metadata') raise GameStateException(f'dialog {npc.dialog.id} already in metadata') if db.has_graph(f'dialog_{npc.dialog.id.hex}'): logger.warning(f'dialog {npc.dialog.id} already defined') raise GameStateException(f'dialog {npc.dialog.id} already defined') if not nx.is_directed_acyclic_graph(npc.dialog.graph): logger.warning(f'dialog {npc.dialog.id} is not acyclic') raise GameStateException(f'dialog {npc.dialog.id} is not acyclic') if not npc.dialog.start_is_set(): logger.warning(f'dialog {npc.dialog.id} has no starting point') raise GameStateException(f'dialog {npc.dialog.id} has no starting point') db_dialog_graph = db.create_graph(f'dialog_{npc.dialog.id.hex}') if not db_dialog_graph.has_vertex_collection('interactions'): logger.debug(f'interaction vertex collection does not exist, creating it.') db_dialog_graph.create_vertex_collection('interactions') conversation = db_dialog_graph.create_edge_definition( edge_collection='conversation', from_vertex_collections=['interactions'], to_vertex_collections=['interactions'] ) # add all dialog nodes for interaction_vertex in set(npc.dialog.start.all_path_nodes()): logger.debug(f'inserting interaction vertex {repr(interaction_vertex)}') db_dialog_graph.insert_vertex('interactions', json.dumps(interaction_vertex, cls=MarugotoEncoder)) dia_visited = {} def dialog_traversal(s, i): """ Walk through the game path from a starting source :param s: starting waypoint for traversal :param i: dict containing identified steps """ for successor in npc.dialog.graph.successors(s): logger.debug(f'successor {repr(successor)} for {repr(s)}') if s not in i.keys(): logger.debug(f'{repr(s)} is new, adding it') i[s] = [] if successor not in i[s]: if successor.task: logger.debug(f'{repr(successor)} has {repr(successor.task)}, adding to tasks') task_dump = json.loads(json.dumps(successor.task, cls=MarugotoEncoder)) task_dump['_key'] = successor.task.id.hex task_dump['for'] = successor.id.hex if not tasks.find(task_dump): tasks.insert(json.dumps(task_dump)) logger.debug(f'inserting interaction edge {repr(s)} to {repr(successor)}') conversation.insert({ '_key': f'{s.id.hex}-{successor.id.hex}', '_from': f'interactions/{s.id.hex}', '_to': f'interactions/{successor.id.hex}' }) dia_visited[s].append(successor) dialog_traversal(successor, i) logger.debug(f'traversing dialog graph for {npc.first_name} {npc.last_name}') dialog_traversal(npc.dialog.start, dia_visited) logger.debug(f'inserting dialog {npc.dialog.id.hex}') db_dialogs.insert({ '_key': npc.dialog.id.hex, 'start': npc.dialog.start.id.hex }) wp_visited = {} def game_traversal(s, i): """ Walk through the game path from a starting source :param s: starting waypoint for traversal :param i: dict containing identified steps """ for successor in game.graph.successors(s): logger.debug(f'successor {repr(successor)} for {repr(s)}') if s not in i.keys(): logger.debug(f'{repr(s)} is new, adding it.') i[s] = [] if successor not in i[s]: for task in successor.tasks: logger.debug(f'{repr(successor)} has {repr(task)}, adding to tasks') task_dump = json.loads(json.dumps(task, cls=MarugotoEncoder)) task_dump['_key'] = task.id.hex task_dump['for'] = successor.id.hex if not tasks.find(task_dump): tasks.insert(json.dumps(task_dump)) logger.debug(f'waypoint interaction edge {repr(s)} to {repr(successor)}') weight = game.graph.edges[s, successor]['weight'] if 'weight' in game.graph.edges[s, successor] and game.graph.edges[s, successor]['weight'] else None path.insert({ '_key': f'{s.id.hex}-{successor.id.hex}', '_from': f'waypoints/{s.id.hex}', '_to': f'waypoints/{successor.id.hex}', 'weight': weight }) wp_visited[s].append(successor) game_traversal(successor, i) logger.debug(f'traversing game graph for {game.title}') game_traversal(game.start, wp_visited) logger.debug(f'inserting game {game.title}') db_games.insert({ '_key': game.title, 'start': game.start.id.hex, 'image': base64.encodebytes(game.image) if game.image else None, 'creator': creator })