Скрипт конфигурации импорта

Описание скрипта

Скрипт конфигурации импорта определяет параметры загрузки в систему данных из внешних источников (внешней системы или файла).

Место настройки скрипта

Место настройки скрипта: форма добавления и редактирования конфигурации импорта.

Структура скрипта

Подробное описание файла конфигурации см. Файл конфигурации импорта.

Скрипт конфигурации импорта представляет собой xml-файл установленного образца.

Все тэги в конфигурации должны идти в определенном порядке и вложенности:

  • <config> — основной тег конфигурации, в него вложены все остальные теги. В параметрах тега задаются основные параметры импорта.

    • <mode> — указывает режим импорта, общий для всех классов. В одной конфигурации импорта можно указывать сразу несколько режимов. Каждый режим прописывается отдельный теге.

      • CREATE — только создание объектов.
      • UPDATE — только обновление существующих объектов.
      • EMPTY — без создания объектов и обновления существующих объектов. В режиме EMPTY выполняются только: script-filter, вычисление значений атрибутов, script-customizer, remove-customizer.
    • <gui-parameter> — позволяет запросить у пользователя дополнительные параметры импорта (строку и файл), которые можно использовать в конфигурации импорта. Тегов <gui-parameter> может быть несколько в одной конфигурации.
    • <parameter> — задает некоторую константную величину, которую можно в дальнейшем использовать в конфигурации. В отличие от <gui-parameter> значение не запрашивается у пользователя.
    • <class> — объявляет описание импортируемых объектов.

      Параметры:

      • name — название класса импортируемых объектов.

      • threads-number — количество потоков, которыми будут обрабатываться элементы из внешнего источника. Переопределяет количество потоков, которые были объявлены в аналогичном параметре тега <config>.

      • log-column-name — название колонки, содержимое которой будет выводиться в круглых скобках в сообщениях импорта после "ID=". Название колонки ищется среди колонок описания источника, указанного в параметре "name". Если значение параметра log-column-name не заполнено или указанная колонка отсутствует, то пользовательский текст выводиться не будет.

      Вложенные теги:

      • <mode> — указывает режим импорта для класса импортируемых объектов, в котором объявлен тег. Указанный режим переопределяет глобальный режим, объявленный в теге <mode>, вложенном в <config>
      • <parameter> — задает некоторую константную величину, которую можно в дальнейшем использовать в конфигурации или переопределяет значение параметра, объявленное в теге <parameter>, вложенном в <config>. Чтобы переопределить значение параметра, нужно указать у него точно такое же значение параметра "name".
      • <*-data-source> — параметры источника данных, вместо * указывается тип источника. В одной конфигурации импорта может быть описано несколько источников данных. Каждый источник должен быть описан в рамках отдельного тега <class>.

        Каждая колонка импортируемых данных описывается в теге <column>, вложенном в <*-data-source>.

      • <*-filter> — описывает фильтрацию и сортировку объектов из источника (вместо * указывается тип фильтра):

        • <hierarchical-filter> определяет необходимость иерархической сортировки импортируемых строк:

          <hierarchical-filter parent-column="parent"/>

        • <column-notempty-filter> указывает фильтр, исключающий из импорта строки с пустым значением указанной колонки в источнике данных:

          <column-notempty-filter column="title"/>

        • <id-prefix> позволяет модифицировать значение id-column и parent-column, дописывая к ним в качестве префикса указанное значение. Это позволяет загружать данные из нескольких источников с одинаковыми внешними идентификаторами:

          <id-prefix prefix="prefix-value"/>

        • <script-filter> указывает фильтр на основе скрипта (в приведенном примере noSkip — имя параметра заданного тегом <parameter>):

          <script-filter mime-type="">return item.properties.id != "skip" || "true" == parameters.noSkip</script-filter>

      • <*metaclass_resolver> — указывает стратегию определения класса и типов создаваемых объектов:

        • <constant-metaclass-resolver> задает конкретный метакласс создаваемых объектов, т.е. все объекты будут созданы с этим метаклассом:

          <constant-metaclass-resolver metaclass="ou$forTest" />

        • <by-column-metaclass-resolver> определяет тип создаваемого объекта по значению колонки импортируемых данных:

          <by-column-metaclass-resolver metaclass="ou" case-column="caseColumn"/>

        • <column-metaclass-resolver> определяет тип создаваемого объекта по значению колонки импортируемых данных.

          В отличие от by-column-metaclass-resolver в колонке должен содержаться полный идентификатор метакласса

          <column-metaclass-resolver default-metaclass="ou$example" column="сolumn"/>

        • <script-metaclass-resolver> определяет тип создаваемого объекта скриптом, на основании входящих данных:

          <script-metaclass-resolver mime-type="application/x-groovy">return item.properties.columnName</script-metaclass-resolver>

      • <*-searcher> — позволяет найти существующий объект, соответствующий строке импортируемых данных:

        • <object-searcher> задает простое правило поиска объекта по значению атрибута объекта. Объекты ищутся среди объектов с заданным классом /типом:

          <object-searcher attr="idHolder" metaclass="ou$forTest"/>

        • <complex-object-searcher> задает последовательность поиска объекта по нескольким атрибутам объекта или по различным классам /типам:

          <complex-object-searcher>
             <object-converter attr="idHolder" metaclass="ou$forTest"/>
             <object-converter attr="title" metaclass="ou"/>
             <script-converter>a + b</script-converter>
          </complex-object-searcher>
        • <script-object-searcher> позволяет находить объект по сложной логике поиска:

          <script-object-searcher mime-type="application/x-groovy"> a + b </script-object-searcher>

      • <attr> — присваивает атрибуту объекта значение из колонки источника импорта.

        • <include-mode> — в теге задается режим, в котором необходимо заполнять (изменять) атрибут.
        • <exclude-mode-mode> — в теге задается режим, в котором не требуется заполнять (изменять) атрибут.
        • <...-converter> — преобразует значение из источника в какой-либо специальный вид.

          Если не указан, то преобразователь будет определен на основе метаинформации.

          • <boolean-converter> преобразует в логическое значение:

            <boolean-converter true-value="goodValue" />

            Параметр true-value задает значение соответствующее "истине".

          • <object-converter> преобразует в объект (если найдено более одного значения, то возвращает произвольный объект):

            <object-converter attr="idHolder" metaclass="ou"/>

            Параметры:

            • attr — имя атрибута, по которому производится поиск объекта;
            • metaclass — код метакласса, среди объектов которого производится поиск;
            • required — определяет, нужно обязательно искать объекты (по умолчанию "required" = true);
            • removed — определяет, нужно ли искать архивные объекты. Если не указан, то ищутся все объекты.
          • <script-converter> задает произвольное правило преобразования значения (скрипт)

            <script-converter mime-type="application/x-groovy"> a + b </script-converter>

            Параметр mime-type — тип скрипта.

          • <collection-converter> преобразует коллекцию значений (для атрибутов типа "Набор ссылок на бизнес-объект" и "Набор элементов справочника") — определяет соответствие для каждого объекта во входящих данных и перенаправляет преобразование к конкретному конвертеру:

            <collection-converter delimiter=",">
                <object-converter/>
                <script-converter></script-converter>
            </collection-converter>

            Параметр delimiter — символ, которым отделены объекты.

          • <complex-object-converter> преобразует в объект. Задает одно или более правил поиска объекта complex-object-converter и выполняет указанные в нем правила последовательно. В порядке определения в xml конвертер переходит к следующему правилу, только если предыдущий вернул null:

            <complex-object-converter>
                <object-converter attr="idHolder" metaclass="ou"/>
                <script-converter></script-converter>
            </complex-object-converter>
          • <datetime-converter> преобразует в дату или дату /время (во внешнем источнике дата может храниться в виде строки):

            <datetime-converter format="yyyy-MM-dd"/>

            <datetime-converter format="yyyy-MM-dd HH:mm:ss"/>

            <datetime-converter format="yyyy-MM-dd hh:mm:ss a"/>

            Параметр format — формат преобразования.

          • <case-list-converter> преобразует в набор типов класса

            <case-list-converter delimiter="," class="ou">

            Параметры:

            • delimiter — символ, которым отделены объекты;
            • class — класс, среди типов которого производится поиск типов.
          • <time-interval-converter> преобразует во временной интервал:

            <time-interval-converter interval="SECOND">

            Параметр interval — используемая единица измерения, возможные значения: SECOND, MINUTE, HOUR, DAY, WEEK.

          • <double-converter> преобразует в вещественное число:

            <double-converter/>

          • <integer-converter> преобразует в целое число:

            <integer-converter/>

          • <hyperlink-converter> преобразует в гиперссылку:

            <hyperlink-converter delimiter=";"/>

            Параметр delimiter — символ, которым разделены названия ссылки и URL ссылки.

          • <ad-image-converter> указывает, что нужно импортировать картинки из AD:

            <ad-image-converter title='pic.jpg' mimeType='image/jpeg' eraseOld='true'/>

            Параметры:

            • title — название загружаемого файла.
            • mimeType — формат хранения (Mime Type) загружаемого файла.
            • eraseOld — определяет, нужно ли удалять старое значение атрибута, если его значение не пусто, а новое значение пусто.
          • <string-converter> преобразует в строку:

            <string-converter trim="true"/>

            Параметр trim определяет, нужно ли удалять пробелы в начале и конце строки

          • <localized-column> используется только для системного локализованного атрибута "title" и указывает в какой язык какое значение нужно сохранить. Если тег указан, а "title" не локализован, то тег игнорируется:

            <localized-column lang="ru" column="title_ru"/>

            <localized-column lang="en" column="title_en"/>

            <localized-column lang="client" column="title_client"/>

            Параметры:

            • lang — язык, в который нужно сохранить значение из источника. Возможные значения: "ru", "en", "client".
            • column — название колонки, из которой надо взять значение.
      • <metaclass-attrs> — используется для импорта пользовательских атрибутов, объявленных в конкретном классе /типе. Атрибуты можно группировать по классам /типам.
      • <...-customizer> — используется для дополнительной обработки значений и объектов во время импорта:

        • <remove-customizer> помещает в архив все объекты, которые не участвуют в импорте, и при этом иерархически находятся в объекте с указанным uuid:

          <remove-customizer hierarchy-root="obj-uuid"/>.

        • <script-customizer> позволяет производить преобразования по сложной логике, определенной в скрипте. Во вложенных тегах указывается скриптовая логика обработки. Каждый из вложенных тегов соответствует какому-то из этапов импорта, соответственно и скрипт, указанный в теге, будет выполнятся на соответствующем этапе импорта:

          <script-customizer>

          <before-import></before-import>

          <before-process-item>a+b</before-process-item>

          <before-process>a+b</before-process>

          <after-process>a+b</after-process>

          <after-import></after-import>

          </script-customizer>

        • <timer-customizer> позволяет импортировать прямой счетчик времени:

          <timer-customizer attr="totalTimeTimer" column="timer" />

        • <backtimer-customizer> позволяет импортировать обратный счетчик времени:

          <backtimer-customizer

          attr="timeAllowanceTimer"

          allowance-column="backTimer"

          deadline-column="deadlineTime"

          deadline-column-format="dd.MM.yyyy HH:mm:ss"/>

Когда выполняется скрипт

При запуске импорта.

Результат выполнения скрипта

Загрузка в систему данных из внешних источников (внешней системы или файла).

Переменные и их значения

Описание переменных и примеры использования:

  • parameters — список параметров, определенных в конфигурации импорта.

    def stringValue = parameters.get('имя параметра')

  • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.

    Переменная storage поддерживает только 1 поток (т. е. когда в тэге <class> параметр threads-number = 1).

    Скрипт 1: Записывает значение параметра в storage

    storage.put('имя параметра', значение)

    Скрипт 2: Извлекает значение параметра из storage

    def value = storage.get('имя параметра');

  • ctx — контекст импорта.

    Получение значения выражения (параметра):

    def value = ctx.evaluate(parameters.get('имя параметра')

    Логирование:

    ctx.getLogger().info("Привет!")

  • item — строка импортируемых данных.

    Получение значения столбца:

    def value = item.properties.getProperty(имя столбца)

    Установка значения столбца:

    item.properties.setProperty("removed", false)

  • subject — найденный объект (если использовался objectSearcher).

Переменные контекста для <script-customizer>:

  • <before-process-item> — содержит скрипт, который вызывается перед формированием свойств бизнес-процесса.

    В скрипте доступны глобальные переменные:

    • item (для чтения /записи) — строка импортируемых данных;
    • subject (только для чтения) — проимпортированный объект;
    • ctx — контекст импорта;
    • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.
  • <before-process>— содержит скрипт, который вызывается перед выполнением бизнес процесса создания /редактирования объекта.

    В скрипте доступны глобальные переменные:

    • item (только для чтения) — строка импортируемых данных;
    • subject (только для чтения) — проимпортированный объект;
    • properties (только для чтения) — свойства бизнес-процесса — итоговые значения атрибутов, которые будут использоваться в бизнес-процессе
    • ctx — контекст импорта;
    • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.
  • <after-process>— содержит скрипт, который вызывается после выполнения бизнес процесса создания /редактирования объекта.

    В скрипте доступны глобальные переменные:

    • item (для чтения /записи) — строка импортируемых данных;
    • subject (только для чтения) — проимпортированный объект;
    • ctx — контекст импорта;
    • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.
  • <after-import>— содержит скрипт, который вызывается один раз после импорта всех объектов.

    В скрипте доступны глобальные переменные:

    • ctx — контекст импорта;
    • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.

Переменные контекста для <script-converter>:

  • ctx — контекст импорта;
  • item — строка импортируемых данных;
  • value — конвертируемое значение;
  • parameters — параметры импорта;
  • subject — импортируемый объект;
  • storage — значение, передаваемое из одного скрипта в другой, в рамках конфигурации импорта.

Переменные контекста для<script-filter>:

  • ctx — контекст импорта;
  • item — строка импортируемых данных;
  • parameters — параметры импорта.

Особенности скрипта

  • Если в рамках <script-filter> необходимо обратится к базе данных (найти и/или изменить объект) utils.edit(), то нужно использовать его внутри транзакции:

    <script-filter>
       api.tx.call({utils.get("root$101")})
       return true
    </script-filter>
  • Все блоки <script-customizer> выполняются в транзакции, поэтому использовать ap.tx.call({}) не нужно, может приводит к ошибкам, когда объект, который изменялся в процессе импорта, нельзя изменить до перезапуска.
  • При использовании в <script-customizer> метода utils.edit() действия по событиям не выполняются.

Пример скрипта

1. Фильтр-скрипт для импорта только части запросов из файла:

<script-filter>
        
//ПАРАМЕТРЫ----------------------------------------------
// Колонка с идентификаторами
def COLUMN_NAME = 'number'; 
// Номера запросов, которые будут проимпортированы
// В данном примере проимпортируются запросы с номером 108,109,111,113,114,115
def CALL_NUMBERS = [].plus(108..109).plus(111).plus(113..115);  
//ОСНОВНОЙ БЛОК------------------------------------------
checkedValue = item.properties.getProperty(COLUMN_NAME)
try
{
return CALL_NUMBERS.contains(checkedValue.toInteger())
}
catch(NumberFormatException e)
{
api.utils.throwReadableException("Невозможно преобразовать %s к int", checkedValue)
return false
}
</script-filter>

2. Скрипт-кастомайзер для замены значения колонки, указанного в источнике (описана в <column>). Если в колонке author указан логин суперпользователя system, то возвращаем null:

<script-customizer>
<before-process-item><![CDATA[
def login = item.properties.author
if("system" == login)
{
item.properties.setProperty("author", null)
}
]]></before-process-item>
</script-customizer>

3. Скрипт-кастомайзер, дописывающий значение в текстовый атрибут:

<script-customizer>
<after-process><![CDATA[
def props = [:];
if(!api.string.isEmptyTrim(item.properties.AddInfo))
{
props.AddInfo = (subject.AddInfo ? subject.AddInfo + '\n' : '')  + item.properties.AddInfo
}
if(!props.isEmpty())
{
utils.edit(subject, props)
}
]]></after-process>
</script-customizer>

4. Конвертор для импорта коллекции объектов:

<collection-converter delimiter=",">
  <script-converter><![CDATA[
      def titleMap = [
      'консультация' : 'Консультации по использованию ПО',
      'доработка' : 'Доработка',
      'изменение настроек ИС' : 'Изменение настроек ИС'];
  return utils.get('closureCode', ['title' : titleMap[value]])
]]></script-converter>
</collection-converter>

5. Конвертор для импорта логинов из LDAP без домена. В тег <attr name="login" column="login"> необходимо вставить скрипт (@CORP.RU — домен, с которым импортируются сотрудники):

<attr name="login" column="login">
<script-converter>
<![CDATA[
try
{
return value.minus('@CORP.RU')
}
catch (ArrayIndexOutOfBoundsException e)
{
return ''
}
]]>
</script-converter>
</attr>

6. Базовая конфигурация импорта отделов:

<?xml version="1.0" encoding="UTF-8"?>
<config
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../../target/generated-sources/jaxb/advimport/schema1.xsd"
save-log="true">

<!-- РЕЖИМ ИМПОРТА: -->
<mode>CREATE</mode>

<!-- ПАРАМЕТРЫ, запрашиваемые на форме запуска импорта: -->
<gui-parameter name="file" type="FILE"
   title="Файл для импорта отделов в формате csv" />

<!-- ДОПОЛНИТЕЛЬНЫЕ ПАРАМЕТРЫ: -->
  <!-- класс импортируемых отделов-->
  <parameter name="ouMetaClass">ou$ouImport</parameter>
  
  <class name="ou" threads-number="2">
     <csv-data-source with-header="true" 
       file-name="$file" delimiter=";" encoding="UTF8" 
       url-timeout="60">
        <column name="title" src-key="title"/>
        <column name="removed" src-key="removed"/>
        <column name="removalDate" src-key="removalDate"/>
     </csv-data-source>
     <constant-metaclass-resolver metaclass="${ouMetaClass}"/>
     <object-searcher attr="title" metaclass="ou"/>
     <attr name="title" column="title" />
     <attr name="removed" column="removed" />
     <attr name="removalDate" column="removalDate" >
        <datetime-converter format="yyyy-MM-dd"/>
     </attr>
  </class>
</config>

7. Базовая конфигурация импорта сотрудников:

<?xml version="1.0" encoding="UTF-8"?> 
<!-- В классах отдел и сотрудник должен существовать атрибут типа строка с кодом idHolder -->
<config
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../../target/classes/advimport/schema1.xsd"
save-log="true">

<!-- РЕЖИМЫ ИМПОРТА: -->
<mode>CREATE</mode>
<mode>UPDATE</mode>

<!-- ПАРАМЕТРЫ, запрашиваемые на форме запуска импорта: -->
<gui-parameter name="ouFile" type="FILE" 
   title="Файл для импорта отделов в формате csv" />
<gui-parameter name="empFile" type="FILE" 
   title="Файл для импорта сотрудников в формате csv" />
<!-- ДОПОЛНИТЕЛЬНЫЕ ПАРАМЕТРЫ: -->
<!-- класс отделов, в которые будут импортироваться сотрудники-->
<parameter name="ouMetaClass">ou$ouImport
    </parameter>

<!-- класс импортируемых сотрудников-->
<parameter name="empMetaClass">employee$empImport
    </parameter>

<!-- ПАРАМЕТРЫ класса импортируемых объектов: -->
<class name="ou" threads-number="2">
<csv-data-source with-header="true" file-name="$ouFile" 
    delimiter=";" id-column="id" encoding="UTF8" 
 url-timeout="60">
     
     <column name="id" src-key="id"/>
     <column name="title" src-key="title"/>
     <column name="removed" src-key="removed"/>
     <column name="removalDate" src-key="removalDate"/>
</csv-data-source>

<constant-metaclass-resolver metaclass="${ouMetaClass}"/>
<object-searcher attr="idHolder" metaclass="${ouMetaClass}"/>
<attr name="title" column="title" />
<attr name="removed" column="removed" />
<attr name="idHolder" column="id" />
<attr name="removalDate" column="removalDate" >        
   <datetime-converter format="yyyy-MM-dd"/>
</attr>
</class>

<class name="importEmployee" threads-number="1">
<csv-data-source with-header="true" file-name="$empFile" 
  delimiter=";" id-column="id" encoding="UTF8" 
  url-timeout="60">
   <column name="id" src-key="id"/>
   <column name="parent" src-key="parent"/>
   <column name="lastName" src-key="lastName"/>
   <column name="firstName" src-key="firstName"/>
   <column name="middleName" src-key="middleName"/>
</csv-data-source>

<constant-metaclass-resolver metaclass="${empMetaClass}"/>
<object-searcher attr="idHolder" metaclass="${empMetaClass}"/>
<attr name="lastName" column="lastName" />
<attr name="firstName" column="firstName" />
<attr name="middleName" column="middleName" />
<attr name="parent" column="parent" >
<object-converter attr="idHolder"
   metaclass="${ouMetaClass}" required="true" />
</attr>
<attr name="idHolder" column="id" />
</class>
</config>