Пример #1
0
async def create_import_db(dataset: List[dict], conn: PG) -> int:
    query = imports_table.insert().returning(imports_table.c.import_id)
    import_id = await conn.fetchval(query)

    citizen_rows = []
    relative_rows = []

    for item in dataset:
        citizen = {
            **item,
            "import_id":
            import_id,
            "birth_date":
            datetime.strptime(item["birth_date"], DATE_FORMAT).date(),
        }
        relatives = citizen.pop("relatives")
        citizen_rows.append(citizen)

        for relative_id in relatives:
            relative_rows.append({
                "import_id": import_id,
                "citizen_id": citizen["citizen_id"],
                "relative_id": relative_id,
            })

    if citizen_rows:
        query = citizens_table.insert().values(citizen_rows)
        await conn.execute(query)

    if relative_rows:
        query = relations_table.insert().values(relative_rows)
        await conn.execute(query)

    return import_id
Пример #2
0
    async def add_relatives(conn, import_id, citizen_id, relative_ids):
        if not relative_ids:
            return

        values = []
        base = {'import_id': import_id}
        for relative_id in relative_ids:
            values.append({
                **base, 'citizen_id': citizen_id,
                'relative_id': relative_id
            })

            # Обратная связь не нужна, если житель сам себе родственник
            if citizen_id != relative_id:
                values.append({
                    **base, 'citizen_id': relative_id,
                    'relative_id': citizen_id
                })
        query = relations_table.insert().values(values)

        try:
            await conn.execute(query)
        except ForeignKeyViolationError:
            raise ValidationError({
                'relatives':
                (f'Unable to add relatives {relative_ids}, some do not exist')
            })
Пример #3
0
async def create_import(db: PG, citizens: List[dict]) -> int:
    async with db.transaction() as conn:
        query = imports_table.insert().returning(imports_table.c.import_id)
        import_id = await conn.fetchval(query=query)

        citizen_rows = make_citizen_rows(import_id=import_id, citizens=citizens)
        relation_rows = make_relation_rows(import_id=import_id, citizens=citizens)

        chunked_citizen_rows = chunk_list(iterable=citizen_rows, size=MAX_CITIZENS_PER_INSERT)
        chunked_relation_rows = chunk_list(iterable=relation_rows, size=MAX_RELATIONS_PER_INSERT)

        query = citizens_table.insert()
        for chunk in chunked_citizen_rows:
            await conn.execute(query.values(list(chunk)))

        query = relations_table.insert()
        for chunk in chunked_relation_rows:
            await conn.execute(query.values(list(chunk)))

        return import_id
Пример #4
0
async def add_relatives(conn: SAConnection, import_id: int, citizen_id: int,
                        relatives: Iterable[int]) -> None:
    """
    Добавляет записи в таблицу родственных связей (relation_table).

    :param conn: объект соединения
    :param import_id: идентификатор выгрузки
    :param citizen_id: идентфикатор жителя
    :param relatives: идентификаторы родственников для добавления
    :raise ValidationError
    """
    values = []
    for relative_id in relatives:
        values.append({
            "import_id": import_id,
            "citizen_id": citizen_id,
            "relative_id": relative_id,
        })

        if citizen_id != relative_id:
            values.append({
                "import_id": import_id,
                "citizen_id": relative_id,
                "relative_id": citizen_id,
            })

    query = relations_table.insert().values(values)

    try:
        await conn.execute(query)
    except ForeignKeyViolationError:
        raise ValidationError(
            message="Unable to add relatives {0}, some do not exist".format(
                relatives),
            field_name="relatives",
        )
Пример #5
0
    async def post(self):
        # Транзакция требуется чтобы в случае ошибки (или отключения клиента,
        # не дождавшегося ответа) откатить частично добавленные изменения.
        async with self.pg.transaction() as conn:
            # Создаем выгрузку
            query = imports_table.insert().returning(imports_table.c.import_id)
            import_id = await conn.fetchval(query)

            # Генераторы make_citizens_table_rows и make_relations_table_rows
            # лениво генерируют данные, готовые для вставки в таблицы citizens
            # и relations на основе данных отправленных клиентом.
            citizens = self.request['data']['citizens']
            citizen_rows = self.make_citizens_table_rows(citizens, import_id)
            relation_rows = self.make_relations_table_rows(citizens, import_id)

            # Чтобы уложиться в ограничение кол-ва аргументов в запросе к
            # postgres, а также сэкономить память и избежать создания полной
            # копии данных присланных клиентом во время подготовки - используем
            # генератор chunk_list.
            # Он будет получать из генератора make_citizens_table_rows только
            # необходимый для 1 запроса объем данных.
            chunked_citizen_rows = chunk_list(citizen_rows,
                                              self.MAX_CITIZENS_PER_INSERT)
            chunked_relation_rows = chunk_list(relation_rows,
                                               self.MAX_RELATIONS_PER_INSERT)

            query = citizens_table.insert()
            for chunk in chunked_citizen_rows:
                await conn.execute(query.values(list(chunk)))

            query = relations_table.insert()
            for chunk in chunked_relation_rows:
                await conn.execute(query.values(list(chunk)))

        return Response(body={'data': {'import_id': import_id}},
                        status=HTTPStatus.CREATED)