示例#1
0
    def db(self, name='_system', username='******', password='', verify=False):
        """Connect to a database and return the database API wrapper.

        :param name: Database name.
        :type name: str | unicode
        :param username: Username for basic authentication.
        :type username: str | unicode
        :param password: Password for basic authentication.
        :type password: str | unicode
        :param verify: Verify the connection by sending a test request.
        :type verify: bool
        :return: Standard database API wrapper.
        :rtype: arango.database.StandardDatabase
        :raise arango.exceptions.ServerConnectionError: If **verify** was set
            to True and the connection to ArangoDB fails.
        """
        connection = Connection(
            url=self._url,
            db=name,
            username=username,
            password=password,
            http_client=self._http_client
        )
        database = StandardDatabase(connection)

        if verify:  # Check the server connection by making a read API call
            try:
                database.ping()
            except ServerConnectionError as err:
                raise err
            except Exception as err:
                raise ServerConnectionError('bad connection: {}'.format(err))

        return database
示例#2
0
def pytest_generate_tests(metafunc):
    tst_db = global_data['tst_db']
    bad_db = global_data['bad_db']

    tst_dbs = [tst_db]
    bad_dbs = [bad_db]

    if metafunc.config.getoption('complete'):
        tst = metafunc.module.__name__.split('.test_', 1)[-1]
        tst_conn = tst_db._conn
        bad_conn = bad_db._conn

        if tst in {'collection', 'document', 'graph', 'aql', 'index'}:
            # Add test transaction databases
            tst_txn_db = StandardDatabase(tst_conn)
            tst_txn_db._executor = TestTransactionExecutor(tst_conn)
            tst_txn_db._is_transaction = True
            tst_dbs.append(tst_txn_db)

            bad_txn_db = StandardDatabase(bad_conn)
            bad_txn_db._executor = TestTransactionExecutor(bad_conn)
            bad_dbs.append(bad_txn_db)

        if tst not in {'async', 'batch', 'transaction', 'client', 'exception'}:
            # Add test async databases
            tst_async_db = StandardDatabase(tst_conn)
            tst_async_db._executor = TestAsyncExecutor(tst_conn)
            tst_dbs.append(tst_async_db)

            bad_async_db = StandardDatabase(bad_conn)
            bad_async_db._executor = TestAsyncExecutor(bad_conn)
            bad_dbs.append(bad_async_db)

            # Add test batch databases
            tst_batch_db = StandardDatabase(tst_conn)
            tst_batch_db._executor = TestBatchExecutor(tst_conn)
            tst_dbs.append(tst_batch_db)

            bad_batch_bdb = StandardDatabase(bad_conn)
            bad_batch_bdb._executor = TestBatchExecutor(bad_conn)
            bad_dbs.append(bad_batch_bdb)

    if 'db' in metafunc.fixturenames and 'bad_db' in metafunc.fixturenames:
        metafunc.parametrize('db,bad_db', zip(tst_dbs, bad_dbs))

    elif 'db' in metafunc.fixturenames:
        metafunc.parametrize('db', tst_dbs)

    elif 'bad_db' in metafunc.fixturenames:
        metafunc.parametrize('bad_db', bad_dbs)
示例#3
0
def list_courses(
        start: date,
        end: date,
        include: list[EventMutationInterpretation] = Query([
            EventMutationInterpretation.addition,
            EventMutationInterpretation.deletion,
            EventMutationInterpretation.reschedule,
        ]),
        week_types: Optional[list[WeekType]] = Query(None),
        current_user: User = Depends(get_current_confirmed_user),
        settings: Settings = Depends(settings.get),
        db: StandardDatabase = Depends(database.get),
) -> list[Course]:
    """
    {start} is included, {end} is excluded (like python's range())
    """
    end = end or start + timedelta(days=1)
    # Get all of the events
    all_events = [
        batch for batch in db.collection("events").find(
            {"owner_key": current_user.key})
    ]
    all_events: list[Event] = [Event(**event) for event in all_events]
    # Get all of the mutations
    all_mutations = [
        batch for batch in db.collection("event_mutations").find(
            {"owner_key": current_user.key})
    ]
    all_mutations: list[EventMutation] = [
        EventMutation(**mutation) for mutation in all_mutations
    ]
    # filter mutations accordinh to ?include
    all_mutations = [
        mutation for mutation in all_mutations
        if mutation.interpretation in include
    ]

    if week_types == "auto":
        week_types = [get_week_type(start, settings)]

    courses: list[Course] = []
    for day in daterange(start, end, precision="days"):
        # Skip outside of year layout
        if not any(day in year_part for year_part in settings.year_layout):
            continue
        # Skip offdays
        if settings.in_offdays(day):
            continue
        # Get relevant events
        events = [
            event for event in all_events if (
                # (week_type compliance)
                (event.on_even_weeks and WeekType.even in week_types) or
                (event.on_odd_weeks and WeekType.odd in week_types)) and (
                    event.day == day)
        ]

        for event in events:
            course = Course(
                start=datetime.combine(date=day, time=event.start),
                end=datetime.combine(date=day, time=event.end),
                subject_key=event.subject_key,
                location=event.location,
            )

            # Get relevant mutations:
            mutations = [
                mutation for mutation in all_mutations
                if mutation.event_key == event._key and (
                    day in mutation.deleted_in or day in mutation.added_in)
            ]

            for mutation in mutations:
                course.location = mutation.location or course.location
                course.subject_key = mutation.subject_key or course.subject_key
                course_daterange = DatetimeRange(start=course.start,
                                                 end=course.end)
                if mutation.deleted_in:
                    course_daterange ^= mutation.deleted_in
                if mutation.added_in:
                    course_daterange |= mutation.added_in
                if course_daterange.duration:
                    course.start = course_daterange.start
                    course.end = course_daterange.end

            courses += [course]

    return courses
示例#4
0
 def list(self, db: StandardDatabase, current_user: User):
     cursor = db.collection(self.name_pl).find({"owner_key": current_user.key})
     return [self.model_out(**item) for item in cursor]
示例#5
0
def pytest_generate_tests(metafunc):
    test_name = metafunc.module.__name__.split('.test_', 1)[-1]

    dbs = [_db]
    bad_dbs = [_bad_db]

    if metafunc.config.getoption('complete'):

        if test_name in {'collection', 'document', 'graph', 'aql', 'index'}:
            # Add test transaction databases
            tdb = StandardDatabase(_db._conn)
            tdb._executor = TestTransactionExecutor(_db._conn)
            tdb._is_transaction = True
            dbs.append(tdb)

            bad_tdb = StandardDatabase(_bad_db._conn)
            bad_tdb._executor = TestTransactionExecutor(_bad_db._conn)
            bad_dbs.append(bad_tdb)

        if test_name not in {
                'async', 'batch', 'transaction', 'client', 'exception'
        }:
            # Add test async databases
            adb = StandardDatabase(_db._conn)
            adb._executor = TestAsyncExecutor(_db._conn)
            dbs.append(adb)

            bad_adb = StandardDatabase(_bad_db._conn)
            bad_adb._executor = TestAsyncExecutor(_bad_db._conn)
            bad_dbs.append(bad_adb)

            # Add test batch databases
            bdb = StandardDatabase(_db._conn)
            bdb._executor = TestBatchExecutor(_db._conn)
            dbs.append(bdb)

            bad_bdb = StandardDatabase(_bad_db._conn)
            bad_bdb._executor = TestBatchExecutor(_bad_db._conn)
            bad_dbs.append(bad_bdb)

    if 'db' in metafunc.fixturenames and 'bad_db' in metafunc.fixturenames:
        metafunc.parametrize('db,bad_db', zip(dbs, bad_dbs))
    elif 'db' in metafunc.fixturenames:
        metafunc.parametrize('db', dbs)
    elif 'bad_db' in metafunc.fixturenames:
        metafunc.parametrize('bad_db', bad_dbs)
示例#6
0
def is_email_taken(db: StandardDatabase, email: EmailStr) -> bool:
    """
    Checks if the given email is already taken
    """
    return db.collection("users").find({"email": email}).count() > 0
示例#7
0
    def db(
        self,
        name: str = "_system",
        username: str = "root",
        password: str = "",
        verify: bool = False,
        auth_method: str = "basic",
        superuser_token: Optional[str] = None,
    ) -> StandardDatabase:
        """Connect to an ArangoDB database and return the database API wrapper.

        :param name: Database name.
        :type name: str
        :param username: Username for basic authentication.
        :type username: str
        :param password: Password for basic authentication.
        :type password: str
        :param verify: Verify the connection by sending a test request.
        :type verify: bool
        :param auth_method: HTTP authentication method. Accepted values are
            "basic" (default) and "jwt". If set to "jwt", the token is
            refreshed automatically using ArangoDB username and password. This
            assumes that the clocks of the server and client are synchronized.
        :type auth_method: str
        :param superuser_token: User generated token for superuser access.
            If set, parameters **username**, **password** and **auth_method**
            are ignored. This token is not refreshed automatically.
        :type superuser_token: str
        :return: Standard database API wrapper.
        :rtype: arango.database.StandardDatabase
        :raise arango.exceptions.ServerConnectionError: If **verify** was set
            to True and the connection fails.
        """
        connection: Connection

        if superuser_token is not None:
            connection = JwtSuperuserConnection(
                hosts=self._hosts,
                host_resolver=self._host_resolver,
                sessions=self._sessions,
                db_name=name,
                http_client=self._http,
                serializer=self._serializer,
                deserializer=self._deserializer,
                superuser_token=superuser_token,
            )
        elif auth_method.lower() == "basic":
            connection = BasicConnection(
                hosts=self._hosts,
                host_resolver=self._host_resolver,
                sessions=self._sessions,
                db_name=name,
                username=username,
                password=password,
                http_client=self._http,
                serializer=self._serializer,
                deserializer=self._deserializer,
            )
        elif auth_method.lower() == "jwt":
            connection = JwtConnection(
                hosts=self._hosts,
                host_resolver=self._host_resolver,
                sessions=self._sessions,
                db_name=name,
                username=username,
                password=password,
                http_client=self._http,
                serializer=self._serializer,
                deserializer=self._deserializer,
            )
        else:
            raise ValueError(f"invalid auth_method: {auth_method}")

        if verify:
            try:
                connection.ping()
            except ServerConnectionError as err:
                raise err
            except Exception as err:
                raise ServerConnectionError(f"bad connection: {err}")

        return StandardDatabase(connection)
示例#8
0
def is_username_taken(db: StandardDatabase, username: str) -> bool:
    """
    Checks if the given username is already taken
    """
    return db.collection("users").find({"username": username}).count() > 0
示例#9
0
def delete(db: StandardDatabase, game: Game, requester=None):
    """
    delete all objects associated with a game
    :param db: connection
    :param game: target
    :param requester: player requesting the delete
    """
    logger.info(f'delete called for {game.title}')
    db_npcs = db.collection('npcs')
    db_dialogs = db.collection('dialogs')
    tasks = db.collection('tasks')
    games = db.collection('games')
    db_game = next(games.find({'_key': game.title}), None)
    if not db_game:
        logger.warning(f'game {game.title} not in metadata')
        raise GameStateException(f'game {game.title} not in metadata')
    if not db.has_graph(f'game_{game.title}'):
        logger.warning(f'game {game.title} not in metadata')
        raise GameStateException(f'game {game.title} does not exist')
    if db_game['creator'] and not db_game['creator'] == requester:
        raise GameStateException(f'cannot delete game {game.title}, you are not the owner')

    for db_npc in db_npcs.find({'game': f'game_{game.title}'}):
        db_dialog = next(db_dialogs.find({'_key': db_npc['dialog']}), None)
        if not db.has_graph(f"dialog_{db_npc['dialog']}"):
            logger.warning(f"dialog {db_npc['dialog']} does not exist")
            raise GameStateException(f"dialog {db_npc['dialog']} does not exist")
        for k, v in dict(db.graph(f"dialog_{db_npc['dialog']}").traverse(start_vertex=f"interactions/{db_dialog['start']}",
                                                                         direction='outbound',
                                                                         strategy='dfs',
                                                                         edge_uniqueness='global',
                                                                         vertex_uniqueness='global')).items():
            if k == 'vertices':
                for interaction in v:
                    if 'task' in interaction and interaction['task']:
                        db_task = next(tasks.find({'_key': interaction['task']}))
                        if db_task:
                            db.delete_document(db_task)
        db.delete_document(db_dialog)
        db.delete_graph(f"dialog_{db_npc['dialog']}", drop_collections=True)
        db.delete_document(db_npc)

    for node in nx.dfs_tree(game.graph):
        for task in node.tasks:
            db_task = next(tasks.find({'_key': task.id.hex}), None)
            if db_task:
                db.delete_document(db_task)
    db.delete_document(db_game)
    db.delete_graph(f'game_{game.title}', drop_collections=True)
示例#10
0
def create_collection(db: StandardDatabase, name: str, unique_fields: Tuple,
                      _schema: Dict):
    if not db.has_collection(name):
        collection = db.create_collection(name=name, schema=_schema)
        collection.add_hash_index(fields=unique_fields, unique=True)
示例#11
0
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
    })
示例#12
0
def read(db: StandardDatabase, title: str) -> Game:
    """
    load an existing game back from the database
    :param db: connection
    :param title: game title
    :return: game object
    """
    logger.info(f'read called for {title}')
    npcs = []
    db_npcs = db.collection('npcs')
    db_dialogs = db.collection('dialogs')
    for db_npc in db_npcs.find({'game': f'game_{title}'}):
        logger.debug(f'reading back npc {db_npc["_key"]}')
        db_dialog = next(db_dialogs.find({'_key': db_npc['dialog']}), None)
        if not db_dialog:
            logger.warning(f"dialog {db_npc['dialog']} not in metadata")
            raise GameStateException(f"dialog {db_npc['dialog']} not in metadata")
        if not db.has_graph(f"dialog_{db_npc['dialog']}"):
            logger.warning(f"dialog {db_npc['dialog']} does not exist")
            raise GameStateException(f"dialog {db_npc['dialog']} does not exist")

        vertices = []
        path = []
        for k, v in dict(db.graph(f"dialog_{db_npc['dialog']}").traverse(start_vertex=f"interactions/{db_dialog['start']}",
                                                                         direction='outbound',
                                                                         strategy='dfs',
                                                                         edge_uniqueness='global',
                                                                         vertex_uniqueness='global')).items():
            if k == 'vertices':
                for vertex in v:
                    vertices.append(vertex)
            elif k == 'paths':
                for edge in v:
                    path.append(edge)
        interactions = []
        for v in vertices:
            interaction = json.loads(json.dumps(v), cls=MarugotoDecoder)
            logger.debug(f'got {repr(interaction)}')
            for task in db.collection('tasks'):
                if task['for'] == interaction.id.hex:
                    interaction.task = json.loads(json.dumps(task), cls=MarugotoDecoder)
                    logger.debug(f'got {repr(interaction.task)} for {repr(interaction)}')
            interactions.append(interaction)

        start_index = -1
        for e in path:
            if not e['edges']:
                start = next(iter([i for i in interactions if i.id == UUID(e['vertices'][0]['_key'])]), None)
                if start:
                    start_index = interactions.index(start)
                    logger.debug(f'setting start index to {start_index}')
                continue
            for edge in e['edges']:
                source = next(iter([i for i in interactions if i.id == UUID(edge['_from'][13:])]), None)
                if not source:
                    logger.warning(f"malformed source for dialog {db_npc['dialog']}")
                    raise GameStateException(f"malformed source for dialog {db_npc['dialog']}")
                idx = interactions.index(source)
                destination = next(iter([i for i in interactions if i.id == UUID(edge['_to'][13:])]), None)
                if not destination:
                    logger.warning(f"malformed destination for {repr(source)} in dialog {db_npc['dialog']}")
                    raise GameStateException(f"malformed destination for {repr(source)} in dialog {db_npc['dialog']}")
                logger.debug(f'adding follow up interaction {repr(destination)} to {repr(source)}')
                source.add_follow_up(destination)
                logger.debug(f'setting index for {repr(source)} to {idx}')
                interactions[idx] = source

        if start_index == -1:
            logger.warning(f"could not determine start for dialog {db_npc['dialog']}")
            raise GameStateException(f"could not determine start for dialog {db_npc['dialog']}")

        npc = NonPlayableCharacter(db_npc['first_name'], db_npc['last_name'], Dialog(interactions[start_index]))
        npc.salutation = db_npc['salutation']
        npc.mail = db_npc['mail']
        npc.image = base64.decodebytes(db_npc['image']) if db_npc['image'] else None
        logger.debug(f'adding {repr(npc)}')
        npcs.append(npc)

    games = db.collection('games')
    db_game = next(games.find({'_key': title}), None)
    if not db_game:
        logger.warning(f'game {title} not in metadata')
        raise GameStateException(f'game {title} not in metadata')
    if not db.has_graph(f'game_{title}'):
        logger.warning(f'game {title} does not exist')
        raise GameStateException(f'game {title} does not exist')
    vertices = []
    path = []
    for k, v in dict(db.graph(f'game_{title}').traverse(start_vertex=f"waypoints/{db_game['start']}",
                                                        direction='outbound',
                                                        strategy='dfs',
                                                        edge_uniqueness='global',
                                                        vertex_uniqueness='global')).items():
        if k == 'vertices':
            for vertex in v:
                vertices.append(vertex)
        elif k == 'paths':
            for edge in v:
                path.append(edge)
    waypoints = []
    for v in vertices:
        waypoint = json.loads(json.dumps(v), cls=MarugotoDecoder)
        logger.debug(f'got {repr(waypoint)}')
        for task in db.collection('tasks'):
            if task['for'] == waypoint.id.hex:
                waypoint.tasks.remove(UUID(task['_key']))
                task = json.loads(json.dumps(task), cls=MarugotoDecoder)
                logger.debug(f'got {repr(task)} for {repr(waypoint)}')
                waypoint.tasks.append(task)
        if waypoint.interactions:
            for i in waypoint.interactions:
                if isinstance(i, UUID):
                    interaction = next(iter([ita for ita in [d.all_path_nodes() for d in [npc.dialog for npc in npcs]] if ita.id == i]), None)
                    if interaction:
                        logger.debug(f'adding {repr(interaction)} to {repr(waypoint)}')
                        waypoint.interactions.append(interaction)
                    else:
                        logger.warning(f'could not locate interaction {i}')
                        raise GameStateException(f'could not locate interaction {i}')
        waypoints.append(waypoint)

    # glue back the task destinations for waypoints
    for task in [ts for t in [w.tasks for w in waypoints if w.tasks] for ts in t]:
        if task.destination and isinstance(task.destination, UUID):
            destination = next(iter([w for w in waypoints if w.id == task.destination]), None)
            if not destination:
                logger.warning(f'could not find destination {task.destination} for task {task.id} in game {title}')
                raise GameStateException(f'could not find destination {task.destination} for task {task.id} in game {title}')
            logger.debug(f'adding {repr(destination)} to {repr(task)}')
            task.destination = destination
    # glue back interaction destinations for waypoints
    for interaction in [ias for ia in [w.interactions for w in waypoints if w.interactions] for ias in ia]:
        if interaction.destination and isinstance(interaction.destination, UUID):
            destination = next(iter([w for w in waypoints if w.id == interaction.destination]), None)
            if not destination:
                logger.warning(f'could not find destination {interaction.destination} for interaction {interaction.id} in game {title}')
                raise GameStateException(f'could not find destination {interaction.destination} for interaction {interaction.id} in game {title}')
            logger.debug(f'adding {repr(destination)} to {repr(interaction)}')
            interaction.destination = destination

    # glue back interaction and task destinations, and waypoints for dialogs
    for start in [d.start for d in [npc.dialog for npc in npcs]]:

        int_visited = {}

        def interaction_traversal(s, i, l):
            for successor in start.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 s.destination and isinstance(s.destination, UUID):
                        destination = next(iter([w for w in l if w.id == s.destination]), None)
                        if not destination:
                            logger.warning(f'could not find destination {s.destination} for interaction {s.id} in game {title}')
                            raise GameStateException(f'could not find destination {s.destination} for interaction {s.id} in game {title}')
                        logger.debug(f'adding {repr(destination)} to {repr(s)}')
                        s.destination = destination
                    if s.task and s.task.destination and isinstance(s.task.destination, UUID):
                        destination = next(iter([w for w in l if w.id == s.task.destination]), None)
                        if not destination:
                            logger.warning(f'could not find destination {s.destination} for task {s.task.id} in interaction {s.id} in game {title}')
                            raise GameStateException(f'could not find destination {s.destination} for task {s.task.id} in interaction {s.id} in game {title}')
                        logger.debug(f'adding task destination {repr(destination)} to {repr(s.task)} for {repr(s)}')
                        s.task.destination = destination
                    if s.waypoints:
                        wps = []
                        for waypoint in s.waypoints:
                            if isinstance(waypoint, UUID):
                                destination = next(iter([w for w in l if w.id == waypoint]), None)
                                if not destination:
                                    logger.warning(f'could not find waypoint {waypoint} for interaction {interaction.id} in game {title}')
                                    raise GameStateException(f'could not find waypoint {waypoint} for interaction {interaction.id} in game {title}')
                                logger.debug(f'adding {repr(destination)}')
                                wps.append(destination)
                            else:
                                logger.debug(f'adding {repr(waypoint)}')
                                wps.append(waypoint)
                        s.waypoints = wps

                    int_visited[s].append(successor)
                    interaction_traversal(successor, i, l)

        logger.debug(f'starting interaction traversal to glue back waypoints')
        interaction_traversal(start, int_visited, waypoints)

    start_index = -1
    for e in path:
        if not e['edges']:
            start = next(iter([i for i in waypoints if i.id == UUID(e['vertices'][0]['_key'])]), None)
            if start:
                start_index = waypoints.index(start)
                logger.debug(f'settings start index to {start_index}')
            continue
        for edge in e['edges']:
            source = next(iter([i for i in waypoints if i.id == UUID(edge['_from'][10:])]), None)
            if not source:
                logger.warning(f'malformed source for game {title}')
                raise GameStateException(f'malformed source for game {title}')
            idx = waypoints.index(source)
            destination = next(iter([i for i in waypoints if i.id == UUID(edge['_to'][10:])]), None)
            if not destination:
                logger.warning(f'malformed destination for {source} in game {title}')
                raise GameStateException(f'malformed destination for {source} in game {title}')
            logger.debug(f'adding {repr(destination)} to {repr(source)}')
            if 'weight' in edge and edge['weight']:
                source.add_destination(destination, edge['weight'])
            else:
                source.add_destination(destination)
            logger.debug(f'setting index for {repr(source)} to {idx}')
            waypoints[idx] = source

    if start_index == -1:
        logger.warning(f'could not determine start for game {title}')
        raise GameStateException(f'could not determine start for game {title}')

    game = Game(title, base64.decodebytes(db_game['image']) if db_game['image'] else None, waypoints[start_index])
    game.npcs = npcs

    return game
示例#13
0
def pytest_generate_tests(metafunc):
    tst_db = global_data.tst_db
    bad_db = global_data.bad_db

    tst_dbs = [tst_db]
    bad_dbs = [bad_db]

    if global_data.complete:
        test = metafunc.module.__name__.split(".test_", 1)[-1]
        tst_conn = tst_db._conn
        bad_conn = bad_db._conn

        if test in {"aql", "collection", "document", "index"}:
            # Add test transaction databases
            tst_txn_db = StandardDatabase(tst_conn)
            tst_txn_db._executor = TestTransactionApiExecutor(tst_conn)
            tst_dbs.append(tst_txn_db)
            bad_txn_db = StandardDatabase(bad_conn)
            bad_txn_db._executor = TestTransactionApiExecutor(bad_conn)
            bad_dbs.append(bad_txn_db)

            # Add test async databases
            tst_async_db = StandardDatabase(tst_conn)
            tst_async_db._executor = TestAsyncApiExecutor(tst_conn)
            tst_dbs.append(tst_async_db)
            bad_async_db = StandardDatabase(bad_conn)
            bad_async_db._executor = TestAsyncApiExecutor(bad_conn)
            bad_dbs.append(bad_async_db)

            # Add test batch databases
            tst_batch_db = StandardDatabase(tst_conn)
            tst_batch_db._executor = TestBatchExecutor(tst_conn)
            tst_dbs.append(tst_batch_db)
            bad_batch_bdb = StandardDatabase(bad_conn)
            bad_batch_bdb._executor = TestBatchExecutor(bad_conn)
            bad_dbs.append(bad_batch_bdb)

    if "db" in metafunc.fixturenames and "bad_db" in metafunc.fixturenames:
        metafunc.parametrize("db,bad_db", zip(tst_dbs, bad_dbs))

    elif "db" in metafunc.fixturenames:
        metafunc.parametrize("db", tst_dbs)

    elif "bad_db" in metafunc.fixturenames:
        metafunc.parametrize("bad_db", bad_dbs)
示例#14
0
def clean_analyzers(db: StandardDatabase):
    for analyzer in ANALYZER_NAMES:
        db.delete_analyzer(analyzer)
示例#15
0
 def __init__(self, adb: StandardDatabase):
     """
     :param adb: connection to arangodb
     """
     self.adb = adb
     self.collection = adb.collection("leases")