Модули Python

 

Общая информация

«Модули Python» — функциональная возможность, предназначенная для обработки и загрузки данных в систему из произвольных источников, с помощью скриптов Python.

Запуск и обработка результатов работы «Модуля Python» осуществляется с помощью «Агента ETL», подробнее см. раздел «Агент ETL».

Требования к окружению

Для корректной работы Модуля Python, требуется:

Наименование Версии Дополнительная информация
Операционная система Microsoft Windows 7 - 10 Подробные технические требования приведены в разделе «Технические требования ETL».
Операционная система семейства Linux
  • Ubuntu 16.x – 18.x
  • RedHat 7.x или другой Linux
Агент ETL 1.34.21 и выше Подробные технические требования приведены в разделе

«Агент ETL».

Python 3 и выше
Виртуальное окружение Python конфигурируется самостоятельно!

До выбора окружения необходимо подготовить само окружение. Это необходимо сделать на сервере с Агентом ETL.

Порядок установки:

  1. После установки Агента ETL будет создан файл настроек агента settings.json и каталоги модулей Python: extract-modules и machine-learning.
    • Для Windows:
      • они будут созданы в каталоге Агента ETL.
    • Для Linux:
      • settings.json – по пути /etc/agentetl/;
      • каталог /opt/agentetl/python/machine-learning/;
      • каталог /opt/agentetl/python/extract-modules/.
  2. Создать 2 виртуальных окружения: в каталогах machine-learning и extract-modules и установить в них зависимости согласно файлу requirements.txt.
    1. Перейти в каталог machine-learning:
      • cd /opt/agentetl/python/machine-learning/ Для Windows это будет каталог, куда был установлен Агент ETL.
    2. Создать виртуальное окружение:
      • Python –m venv venv (алиас Python может различаться в зависимости от того, что указано в системе, например Python3 или py).
    3. Активировать виртуальное окружение:
      • Windows: venv\Scripts\activate;
      • Linux: source venv/bin/activate.
    4. Установить зависимости согласно файлу requirements.txt:
      • Если необходимо установить дополнительные зависимости, их нужно добавить в файл requirements.txt перед выполнением команды установки.
        Обратите внимание! ​​​openpyxl - приведено как пример установки зависимости​​​​:
        • openpyxl== 3.1.3 - если нужна конкретная версия;
        • openpyxl – если подходит текущая последняя версия;
        • openpyxl>= 3.1.3 – если нужна версия больше указанной.
      • Команда установки зависимостей из файла requirements.txt:pip install -r requirements.txt. В случае необходимости доустановить зависимости, добавляем зависимость в requirements.txt и выполняем команду заново.
    5. Перейти в каталог extract-modules:
      • cd /opt/agentetl/python/extract-modules/.
    6. Выполнить пункты 2.22.4 для каталога extract-modules.
  3. Заполнить settings.json.

Файл представляет из себя json-файл:

{
    "Addr": ":8000",
    "AddrForCallBack": "",
    "TLSCertFile": "",
    "TLSKeyfile": "",
    "Login": "user",
    "Password": "123",
    "LogFile": "log.txt",
    "SaveStatsToFile": "",
    "Servers": [],
    "ETLLogURL": "",
    "ETLLogInterval": 30,
    "ETLSuccessURL": "",
    "Capacity": 200000,
    "Workers": 50,
    "Bulksize": 0,
    "WorkingDir": "",
    "MLPython": "venv\\Scripts\\python3.exe",
    "ExtractPython": "",
    "Databases": {
        "Vertica" : {
            "InMemoryResultRowlomit": 200000,
            "CopyBlockSize": 65536
        }
    }
}

Подробное описание назначения параметров, см. в разделе «Агент ETL» подраздел «Настройка параметров Агента ETL».

​​​​Строки настроек для работы Python:

  • "MLPython": "Путь до исполняемого файла python виртуального окружения каталога machine-learning";
  • "ExtractPython": "Путь до исполняемого файла python виртуального окружения каталога extract-modules".

Пример заполнения данных настроек для Linux:

  • "WorkingDir": "/opt/agentetl";
  • "MLPython": "venv\\Scripts\\python3.exe";
  • "ExtractPython": "venv\\Scripts\\python3.exe".

Обратите внимание! Если параметр "WorkingDir" заполнен, тогда параметры "MLPython" и "ExtractPython" заполняются относительно пути "WorkingDir".

 

Настройка справочника «Модули Python»

Для работы с модулем Python нажмите в верхнем меню «Главное», выберите выпадающий список «Настройки» и перейдите в раздел «Модули Python».

В отобразившемся разделе создайте новый модуль при помощи кнопки «Создать» (1) или «Создать новый элемент копированием текущего» (2).

В отобразившейся форме создания заполнение поле «Наименование». Если необходимо использовать отдельное виртуальное окружение, то нужно заполнить поле «Окружение», подробнее см. подраздел «Создание виртуальных окружений Python».

При нажатии на «Показать все» у поля «Окружение» отобразится форма выбора окружения. При необходимости создания нового окружения нажмите на кнопку «Создать».

В отобразившейся форме заполните поле «Наименование», а в поле «Исполняемый файл python» укажите путь до интерпретатора Python на сервере с «Агентом ETL».

Заполните и закройте форму, выберите окружение.

На вкладке «Скрипт» введите код.

Шаблоны скриптов

В окружении устанавливаются разные библиотеки. Окружение создается на сервере с установленным «Агентом ETL» и путь до него задается с учетом этого. Одновременно может быть развернуто несколько виртуальных окружений Python, каждое из которых содержит свою версию Python и свой набор пакетов.

Примечание — для корректной работы шаблонов кода в справочнике «Модули Python» требуются установить следующие библиотеки в каталог extract-modules, подробнее см. «Создание виртуальных окружений Python»:

  • База данных:
    • psycopg2;
    • sqlalchemy.
  • Веб-сервис:
    • requests.

В качестве примеров скриптов можно использовать шаблоны. Для этого нажмите на кнопку «Заполнить из шаблона» и выберите необходимый вариант, отобразится скрипт:

  • «База данных»:
Скрипт шаблона базы данных

from typing import Dict, Union

from sqlalchemy import create_engine, text

from api import (
	ParametersClass, ParameterClass, ParameterItemsClass, ColumnsClass, TrivialType,
    ColumnClass, DataSourceClass, TableDataClass, TypeDescription, Source
)


def get_params() -> ParametersClass:
    # Создаем несколько параметров для пользователя

    td_str = TypeDescription(precision=0, length=100, contain_time=False, data_type=TrivialType.ttString )
    td_number = TypeDescription(precision=10, length=100, contain_time=False, data_type=TrivialType.ttNumber )
    td_integer = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger )
    td_date = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttDate )
    td_datetime = TypeDescription(precision=0, length=0, contain_time=True, data_type=TrivialType.ttDatetime )

    variables = [
        ParameterClass(index=0, name="is_active", type_desc=td_integer, value=1),
    ]

    # Оборачиваем в VariableItemsClass
    items = ParameterItemsClass(variables)

    return ParametersClass(items=items)


def get_structure_table(params: ParametersClass) -> DataSourceClass:

    is_active = params[0].Value  # Получаем значение для поля "is_active"

    # Базовые колонки
    td = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger )
    td_str = TypeDescription(precision=0, length=100, contain_time=False, data_type=TrivialType.ttString )
    columns = [
	    ColumnClass(index=0, name="Age", type_desc=td),
	    ColumnClass(index=1, name="Name", type_desc=td_str),
    ]


    # Создаем объект таблицы
    columns_class = ColumnsClass(columns)
    table = DataSourceClass(columns=columns_class)

    # Возвращаем созданную таблицу
    return table


def get_data(
	params: ParametersClass,
	table_structure: DataSourceClass,
	source: Source,
	context: Dict[str, Union[int, float, bool, str]],
) -> TableDataClass:

    query_params = {param.Name: param.Value for param in params.Items}

    engine = create_engine(f"postgresql+psycopg2://{source.Login}:{source.Password}@{source.Address}/{source.Database}")
    table = TableDataClass(table_structure)

    with engine.connect() as engine.connection:
        query = text("SELECT Name, Age FROM table_test")

        result = engine.connection.execute(query, query_params)
        rows = result.mappings().all()

        for row in rows:
            data = []
            for column in table.iter_columns():
                data.append(row.get(column.Name))
            table.AddRow(data)

    return table
  • «Веб-сервис»:
Скрипт шаблона веб-сервиса

from typing import Dict, Union

import requests

from api import (
ParametersClass, ParameterClass, ParameterItemsClass, ColumnsClass, TrivialType,
ColumnClass, DataSourceClass, TableDataClass, TypeDescription, Source
)


def get_params() -> ParametersClass:
    # Создаем несколько переменных для пользователя

    td_str = TypeDescription(precision=0, length=100, contain_time=False, data_type=TrivialType.ttString )
    td_number = TypeDescription(precision=10, length=100, contain_time=False, data_type=TrivialType.ttNumber )
    td_integer = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger )
    td_date = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttDate )
    td_datetime = TypeDescription(precision=0, length=0, contain_time=True, data_type=TrivialType.ttDatetime )

    variables = [
        ParameterClass(index=0, name="test_param",  type_desc=td_integer, value=1),
    ]

    # Оборачиваем в VariableItemsClass
    items = ParameterItemsClass(variables)

    return ParametersClass(items=items)


def get_structure_table(params: ParametersClass) -> DataSourceClass:

    # Базовые колонки
    td = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger)
    td_str = TypeDescription(precision=0, length=200, contain_time=False, data_type=TrivialType.ttString)
    columns = [
	    ColumnClass(index=0, name="ID", type_desc=td_str),
	    ColumnClass(index=1, name="Name", type_desc=td_str),
	    ColumnClass(index=2, name="Photo", type_desc=td_str),
	    ColumnClass(index=3, name="Status", type_desc=td_str),
	]


    # Создаем объект таблицы
    columns_class = ColumnsClass(columns)
    table = DataSourceClass(columns=columns_class)

    # Возвращаем созданную таблицу
    return table


def get_data(
	params: ParametersClass,
	table_structure: DataSourceClass,
	source: Source,
	context: Dict[str, Union[int, float, bool, str]],
) -> TableDataClass:

    table = TableDataClass(table_structure)
    response = requests.get(source.Address)
    # https://petstore.swagger.io/v2/pet/findByStatus?status=available

    data = response.json()
    for pet in data[:10]:
    	table.AddRow([
    	    pet['id'],
    	    pet['name'],
    	    pet['photoUrls'][0] if pet['photoUrls'] else '',
    	    pet['status'],
    	])

    return table
  • «Файл»:
Скрипт шаблона файла
from typing import Dict, Union
import re

from api import (
ParametersClass, ParameterClass, ParameterItemsClass, ColumnsClass, TrivialType,
ColumnClass, DataSourceClass, TableDataClass, TypeDescription, Source
)



def get_params() -> ParametersClass:
    # Создаем несколько параметров для пользователя

    td_str = TypeDescription(precision=0, length=100, contain_time=False, data_type=TrivialType.ttString )
    td_number = TypeDescription(precision=10, length=100, contain_time=False, data_type=TrivialType.ttNumber )
    td_integer = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger )
    td_date = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttDate )
    td_datetime = TypeDescription(precision=0, length=0, contain_time=True, data_type=TrivialType.ttDatetime )

    variables = [
        ParameterClass(index=0, name="test_param",  type_desc=td_str, value="some string"),
    ]

    # Оборачиваем в VariableItemsClass
    items = ParameterItemsClass(variables)

    return ParametersClass(items=items)


def get_structure_table(params: ParametersClass) -> DataSourceClass:

    # Базовые колонки
    td = TypeDescription(precision=0, length=0, contain_time=False, data_type=TrivialType.ttInteger)
    td_str = TypeDescription(precision=0, length=100, contain_time=False, data_type=TrivialType.ttString)
    columns = [
    	ColumnClass(index=0, name="Name", type_desc=td_str),
    	ColumnClass(index=1, name="INN", type_desc=td_str),
    	ColumnClass(index=2, name="Phone", type_desc=td_str),
    ]


    # Создаем объект таблицы
    columns_class = ColumnsClass(columns)
    table = DataSourceClass(columns=columns_class)

    # Возвращаем созданную таблицу
    return table


def get_data(params: ParametersClass, table_structure: DataSourceClass, source: Source, context: Dict[str, Union[int, float, bool, str]]) -> TableDataClass:

    table = TableDataClass(table_structure)

    with open(source.Address, 'r', encoding='utf-8') as f:
    	text = f.read()

    table.AddRow([
        re.search('Наименование: (.*)', text).group(0),
        re.search('ИНН: (.*)', text).group(0),
        re.search('Телефон: (.*)', text).group(0),
    ])

    return table

Контракт шаблона

Шаблоны дают представление о том, как должен выглядеть файл Python. В нем подготавливаются 3 функции:

  • get_params;
  • get_structure_table;
  • get_data.
Функции get_params, get_structure_table, get_data
  • get_params

Предоставляет «Modus ETL» список параметров для доступа к данным и их дефолтные значения.

В функции заданы основные типы данных (td_str, td_number, td_integer...) в качестве примера, на их основе можно конструировать свои типы, указывая нужные величины length (длина строки/числа) и precision (точность для дробного числа).

Сами параметры мы помещаем в список variables, каждое значение представляет собой инициализированный класс ParameterClass. Все аргументы инициализации - обязательные.

    variables = [
        ParameterClass(index=0, name="is_active", type_desc=td_integer, value=1),
    ]

Если нам не нужны параметры, то список variables оставляем пустым.

  • get_structure_table

Предоставляет пользователю «Modus ETL»перечень колонок результирующей таблицы.

Список типов задается так же, как и в функции get_params.

Поля помещаем в список columns.

  • get_data

В функции заполняется таблица с данными. Здесь должна находится основная логика по работе с данными. Итогом выполнения функции должна стать возвращаемая таблица - экземпляр класса TableDataClass.

Входящие параметры get_data:

  • params: ParametersClass — параметры сбора данных;
  • table_structure: DataSourceClass — структура результирующей таблицы;
    • Columns: ColumnsClass — список колонок типа ColumnClass.

      • ColumnClass
        • Index: int — индекс колонки (с 0);

        • Name: str — имя колонки;

        • type_desc: TypeDescription — тип колонки:

          • DataType: TrivialType:
            •     ttString = 1;
            •     ttNumber = 2;
            •     ttDate   = 3;
            •     ttDatetime = 4;
            •     ttInteger = 5;
            •     ttUndefined = 6.
  • Length: Optional[int] — длина значения;

  • Precision: Optional[int] — точность значения;

  • ContainTime: bool — содержит время (deprecated):

    • Data: List[List[Union[bool, int, str, float, datetime.datetime, None]]]
    • Get(row: int, col: Union[int, str]) -> Union[bool, int, str, float, datetime.datetime, None] - Возвращает значение ячейки по строке и колонке
    • GetColumn(col: Union[int, str]) -> ColumnClass - Возвращает колонку по индексу или имени
    • IsNull(row: int, col: Union[int, str]) -> bool - Проверяет, является ли ячейка пустой
  • source: Source — параметры источника данных:

    • Address: str — адрес БД, файла, веб-сервиса, и.т.п.;

    • Database: str — база данных;

    • Login: str — логин к БД;

    • Password: str — пароль к БД;

    • Timeout: int — таймаут вызова БД/сервиса;

    • SecureMode: str — параметр подключения;

    • SkipVerify: bool — параметр подключения;

    • MaxConns: int — максимальное число соединений;

    • DefSchema: str — схема подключения;

    • Protocol: str — протокол подключения;

    • AdditionalProperties: Dict[str, str] — дополнительные параметры подключения;

    • FileName: str — имя файла;

    • Kind: str — тип файла;

    • DataId: str — id данных файла во временном хранилище сессии.

  • context: Dict[str, Union[int, float, bool, str]] — произвольные переменные контекста.

При замене уже имеющегося скрипта отобразиться форма с подтверждением замены скрипта шаблоном. Подтвердите замену нажатием на кнопку «Да» либо нажмите на кнопку «Нет» для отмены.

На вкладке «Описание» возможно написать комментарий с описанием модуля.

Нажмите на кнопку «Записать и закрыть» — модуль создан.

Далее при установке правил выгрузки вы сможете установить вид правила «Модуль Python», как описано в разделе «​​​​​​​Настройка правила вида «Модуль python»».