Руководство пользователя Semantic MDM
×
Меню
Индекс

Пример обогащения позиции данными, полученными из ML-сервиса

 
Данный пример наглядно демонстрирует решение следующих задач:
Разработанный groovy-скрипт должен быть размещен в постобработчике на изменение значения атрибута “Полное наименование” справочника “Единый справочник номенклатуры”.
Сценарий работы пользователя: пользователь создает новую позицию и в карточке новой позиции в атрибут “Полное наименование” вводит требуемое полное наименование и нажимает кнопку запуска пост-обработчика. Система выполняет предсказание через ML-сервис, выполняет автоматическое заполнение атрибутов новой позиции. Пользователь корректирует неверно предсказанные значения атрибутов, после чего проводит дозаполнение требуемых атрибутов и публикует пакет изменений.

    

Скрипт постобработчика

 
// Предсказание значений атрибутов по полному наименованию
import groovy.json.JsonSlurper
String postProcessValue(String value){
    // value - изменённое значение
    // @[...] - атрибутика обрабатываемой позиции
    // @[#currentuser][...] - атрибутика пользователя, выполняеющего функцию
    // вернуть строку!=null, если требуется сигнализировать ошибку
    String EI_uid=null;
    String DoubleVal=null;
   
    //Получим полное наименование, которое ввел пользователь
    String FullName = value;
   
    // Получить из сервиса машинного обучения предсказанные значения атрибутов, на основе полного наименование объекта(FULL_NAME)
    HttpURLConnection connection = new URL("https://ml.sdi-solution.ru/extract1?full_name=" +
      ru.sdi.m2.resources.util.StringUtil.urlEncod      e(FullName)).openConnection() as HttpURLConnection;   
  
    //Выполним парсинг JSON, с результатом предсказания
    def rootNode = new JsonSlurper().parseText(connection.inputStream.text);
   
    //Получим гуид группы, в которой требуется создать позицию
    //гуид группы предсказывается атрибутом attr_name=="groupId". Идентификатор даннаго атрибута attr_id='31415926'
    String NodeUid = rootNode.extracted_attrs.find {it.attr_name=="groupId"}.attr_value;
    //Убедимся что предсказанная группа и текущая группа совпадают
    String curNodeUid = support.getItemNodeUid(scriptData.getItemUid(null));
    if (NodeUid != curNodeUid)
      return "Предсказанная машинным обучением группа не совпадает с текущей. Выберите группу с GUID: "+NodeUid;
   
    //Получим список продекларированных атрибутов в предсказанной группе
    List<PropertyDeclarationInfoDto> nodeDeclarations = support.getPropertyDeclarationsByNodeUid(NodeUid,false);
   
    //Получим построитель запроса на изменение значений текущей позиции
    UpdateItemBuilder itemValueUpdates = postOperation.updateItem(scriptData.getItemUid(null));
   
    List<PropertyDeclarationInfoDto> processedDeclarations = [];
   
    //Пройдемся в цикле по извлеченным атрибутам
    for (int i = 1; i < rootNode.extracted_attrs.size(); i++) {
      String PropPath = rootNode.extracted_attrs[i].attr_id;
      String PropValue = rootNode.extracted_attrs[i].attr_value;
 
      //Атрибут "groupId" с идентификатором '31415926' игнорируем, т.к. обработали его ранее
      if (PropPath!='31415926')
      {
        //Если атрибут связь
        int delimPos=PropPath.indexOf('->');
        if (delimPos>0) 
        {
          //Получим гуид агрегации(атрибут связи) из пути
          String AggrPropUid = PropPath.substring(0,delimPos);
         
          PropertyDeclarationInfoDto attrDeclaration = nodeDeclarations.find {element -> element.uuid == AggrPropUid};
          //Если агрегация с таким гуид не существует, то выдаем ошибку
          if (attrDeclaration == null)
            continue; //return 'не удалось найти декларацию атрибута связи с гуид "'+AggrPropUid+'"';
         
          //Для агрегации может быть предсказано несколько вложенных атрибутов,
          //поэтому одну и ту же корневую
          if (!processedDeclarations.contains(attrDeclaration))
          { 
            processedDeclarations.add(attrDeclaration); 
            //Найдем все атрибуты, для обрабатываемой агрегации
            def allAggrs = rootNode.extracted_attrs.findAll {it.attr_id.contains(AggrPropUid)}
            //Получим область поиска агрегации
            List<String> aggrScope = attrDeclaration.scopeNodes;
            if (attrDeclaration.scopeNodes.size()==0)
              return 'Область для атрибута связи "'+allAggrs[0].attr_name.subString(0,allAggrs[0].attr_name.indexOf('->'))+'" не задана';
            String aggrItemUid = getAggregationItemUid(rootNode, AggrPropUid, aggrScope);
           
            //Зададим новое значение для атрибута связи
            if (aggrItemUid!=null
              itemValueUpdates.set(aggrItemUid, attrDeclaration.id)
            else
              itemValueUpdates.clear(attrDeclaration.id);   
              //return 'Не удалось найти агрегируемую позицию "'+PropValue+'" для атрибута "'+allAggrs[0].attr_name.substring(0,allAggrs[0].attr_name.indexOf('->'))+'"';
          }
        }
        //Если атрибут простой
        else
        {
          //Проверим наличие атрибута, если атрибут не декларирован, то выдаем ошибку
          PropertyDeclarationInfoDto attrDeclaration = nodeDeclarations.find {element -> element.uuid == PropPath};
          if ((attrDeclaration==null)||(attrDeclaration.uuid=='98fb1b1d-89e6-475c-b2fb-64e6b5424eaa'))
            continue; //return 'Не удалось найти атрибут с гуид "'+PropPath+'"';
         
          //Зададим новое значение для простого атрибута
          if ((PropValue!=null)&&(PropValue!=""))
            itemValueUpdates.set(PropValue, attrDeclaration.id);
          else
            itemValueUpdates.clear(attrDeclaration.id);
        }
      }
    }
   
    return null;
}
 
//Найти агрегированную позицию по вложенным атрибутам и вернуть ее гуид
//rootNode - JSON с ответом подсистемы машинного обучения
//aggrPath - путь к агрегации из идентификаторов, для которой нужно найти агрегируемую позицию
//aggrAttrId - числовой идентификатор атрибута
String getAggregationItemUid(def rootNode, String aggrPath, List<String> aggrScope) {
  //Проверим что область агрегации где будем искать не пуста
  if (aggrScope.size()==0)
    throw new Exception('не определена область для агрегации "'+aggrPath+'"');
 
  //Получим список вложенных в агрегацию атрибутов
  def aggrAttrs = rootNode.extracted_attrs.findAll {it.attr_id.contains(aggrPath)};
  //Здесь будем хранить список обработанных вложенных агрегаций
  List<String> aggrProcessed = [];
  //Соберем поисковый запрос
  SearchParamsBuilder SearchBuilder=support.findInNodes(aggrScope);
  for (int i = 0; i < aggrAttrs.size(); i++) {
    //Получим полный путь к вложенному атрибуту
    String attrPath = aggrAttrs[i].attr_id;
    //Удалим из пути во вложенный атрибут, путь до агрегации
    String NestedAttr = attrPath.minus(aggrPath+'->');
    if ((aggrAttrs[i].attr_value != '')&&(aggrAttrs[i].attr_value != null)) {
      //Если во вложенном атрибуте есть еще вложенные атрибуты, то обработаем их
      if (NestedAttr.indexOf('->')>0)
      {
        String nestedAggrPropUid = NestedAttr.subString(0,NestedAttr.indexOf('->'));
        if (!aggrProcessed.contains(nestedAggrPropUid))
        {
          //Получим числовой идентификатор свойства
          Long nestedAggrPropId = support.getPropertyId(nestedAggrPropUid);
          //Получим область поиска агрегации
          List<String> nestedAggrScope = support.getPropertyScopeInNode(nestedAggrPropId,root.getMdmNodeId());
          //Получим агрегируемую позицию, найденную по вложенным атрибутам
          String AggregationItemUid=getAggregationItemUid(rootNode, aggrPath+'->'+nestedAggrPropUid, nestedAggrScope);
          //Добавляем критерий поиска
          if (AggregationItemUid!=null)
            SearchBuilder = SearchBuilder.byValue(AggregationItemUid, nestedAggrPropId);
          else
            throw new Exception('не удалось найти позицию для агрегации "'+aggrAttrs[i].nestedAggrPropUid+'"'); 
        }
      }
//Если вложенный атрибут является простым атрибутом и значение не пустое, то добавим его в критерии поиска
      else
      {
        PropertyInfoDto searchedProp = support.getPropertyInfoByUid(NestedAttr);
        String searchedVal = aggrAttrs[i].attr_value.toString();
        //Добавляем критерий поиска
        SearchBuilder = SearchBuilder.byValue(searchedVal, searchedProp.id.toString()); 
      }
    }   
 
    //Выполним поиск по сформированным критериям
    List<String> itemUids = SearchBuilder.byStatus(ItemStatus.Standardized).invoke();
 
    //Если нашлась одна единственная позиция, то возвращаем ее
    if (itemUids.size()==1)
      return itemUids[0];
  }
  //Вернем пустоту, если ничего не нашли
  return null
 
}