Регистрация запуска обработок во внешнем источнике данных

На нашем предприятии эксплуатируется три информационные базы, в которые по просьбам трудящихся постоянно создаются и добавляются новые внешние отчеты и обработки. Некоторые из них устаревают сразу же после их первого использования, другие чуть позже, какие-то используются постоянно.
Давно хотел получить инструмент, позволяющий отслеживать статистику использования внешних отчетов и обработок.

Итак, регистрацию решено производить в таблице базы mysql. Таблица должна хранить время запуска, имя пользователя, название обработки, режим регистрации и имя информационной базы.

CREATE TABLE IF NOT EXISTS `OuterProgsUsage` (
`idRec` int(11) NOT NULL AUTO_INCREMENT,
`MDname` varchar(90) NOT NULL COMMENT 'Имя отчета/обработки',
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`variant` varchar(30) NOT NULL,
`userCode` varchar(50) NOT NULL,
`userName` varchar(90) NOT NULL,
`comment` varchar(12000) NOT NULL,
`action` enum('open','form') NOT NULL,
`ibName` varchar(20) NOT NULL,
`version` varchar(32) NOT NULL,
`prgType` varchar(15) NOT NULL COMMENT '"Обработка" | "Отчет"',
PRIMARY KEY (`idRec`),  KEY `MDname` (`MDname`),
KEY `userCode` (`userCode`),
KEY `MDvariant` (`MDname`,`variant`),
KEY `ibName` (`ibName`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

Далее, в дереве конфигурации добавим внешний источник данных с описанием созданной таблицы

 

 

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

Функция ЗафиксироватьЗапускВнешнейОбработки(
НазваниеОбработки // ОбъектОбработки.Метаданные().Родитель().Имя
, ТипФайла = "Обработка"
, Вариант = ""
, Комментарий = ""
, Действие = "open" // "open"|"form"
) Экспорт

ПараметрыСоединения = Новый ПараметрыСоединенияВнешнегоИсточникаДанных;
ПараметрыСоединения.СтрокаСоединения = "Driver={MySQL ODBC 5.3 Unicode Driver}
|;Server=192.168.xx.xxx
|;Database=base
|;username=us8
|;password=mysqlpwd
|;option=3;";
ПараметрыСоединения.АутентификацияСтандартная = Истина;
ПараметрыСоединения.ИмяПользователя  = "us8";
ПараметрыСоединения.Пароль   = "mysqlpwd";
Вид = ВнешниеИсточникиДанных.ахПротоколИспользованияВнешнихОбработок;
Вид.УстановитьОбщиеПараметрыСоединения(ПараметрыСоединения);
ом = Вид.Таблицы.OuterProgsUsage.СоздатьОбъект();
ом.MDname   = НазваниеОбработки;
ом.prgType  = ТипФайла;
ом.action   = Действие;
ом.comment   = Комментарий;
ом.date   = ТекущаяДата();
ом.userCode  = ПараметрыСеанса.ТекущийПользователь.Код;
ом.userName  = ПараметрыСеанса.ТекущийПользователь.Наименование;
ом.variant   = Вариант;
ом.ibName   = ВРег(НСтр(СтрокаСоединенияИнформационнойБазы(), "Ref"));
Попытка
ом.Записать();
Исключение
КонецПопытки;
КонецФункции

Из тестовой обработки или Консоли кода(ИР) вызовем функцию:

тестовый вызов функции

 

 и убедимся, что в таблице OuterProgsUsage появилась соответствующая запись:

запись о регистрации

 Механизм работает, теперь нужно в каждую обработку из справочника ВнешниеОбработки добавить строку вызова функции ЗафиксироватьЗапускВнешнейОбработки. Можно, конечно, сохранять файлы по одному на диске и редактировать их в соответствующем (понятно почему?) конфигураторе, но так неинтересно. Я пошел своим путем. Сначала был написан скрипт на языке 1Script от уважаемого Evil Beaver. Скрипт умеет выгружать файлы внешних обработок из справочника ВнешниеОбработки и загружать измененные файлы обратно в справочник.

ExtRepsLoad.os

Из-за размера скрипта добавлю его в файлы.

Для регистрации факта открытия/запуска обработки, вызов функции ЗафиксироватьЗапускВнешнейОбработки решил добавить в конец каждого модуля объекта отчета/обработки.

///////// РЕГИСТРАЦИЯ ЗАПУСКА ///////////// #$#20241019#$# - ключ для исключения повторного добавления фрагмента
Попытка
ФайлОбработки = Новый Файл(ИспользуемоеИмяФайла);
ИспользуемоеИмя = ?(Найти(ВРег(ИспользуемоеИмяФайла), ".TMP")>0, ИспользуемоеИмя, ФайлОбработки.Имя);
ВидФайла = ?(Найти(ВРег(ИспользуемоеИмя), ".ERF")>0 ,"Отчет", "Обработка");
Комментарий = "";
ИспользуемоеИмя = Лев(ИспользуемоеИмя,  Найти(ИспользуемоеИмя, ".")-1);
тИмяОбр = Метаданные().Имя;
тСиноним = Метаданные().Синоним;
Если Лев(ВРег(тИмяОбр),5) = "ВНЕШН" Тогда
Комментарий = "Имя обработки: "+тИмяОбр+""+?(ПустаяСтрока(тСиноним),", Пустой синоним", ", Синоним: """+тСиноним+"""");
ИмяОбработки = ИспользуемоеИмя;
Иначе
ИмяОбработки = тИмяОбр;
КонецЕсли;
ВнешниеИсточникиДанных.ахПротоколИспользованияВнешнихОбработок.Таблицы.OuterProgsUsage.ЗафиксироватьЗапускВнешнейОбработки(ИмяОбработки, ВидФайла,,Комментарий);
Исключение
ЗаписьЖурналаРегистрации("Ошибка регистрации исполнения внешней обработки", УровеньЖурналаРегистрации.Предупреждение, ,ЭтотОбъект.Метаданные().ПолноеИмя(), ОписаниеОшибки());
КонецПопытки;
//////////////////////////////////////////

Опять же, вручную можно таким образом обработать 20-50 файлов, у меня их оказалось около 500. Поэтому, процесс добавления, хоть и однократный, тоже захотелось автоматизировать, а чтобы труд даром не пропал, результатом поделиться с сообществом.

Для того, чтобы добраться до модуля объекта обработки, ее нужно распаковать программой v8Unpack, правда, бинарников по ссылке не найти, но их можно вынуть из макетов мощного инструмента v8Reader.

В тестовом каталоге e:ooo были созданы каталоги

  • reps — для извлеченных из справочника ВнешниеОбработки файлов
  • in — для тех-же файлов, приготовленных для распаковки
  • out — для каталогов распакованных v8Unpack обработок
  • ok — для собранных файлов обработок, готовых к загрузке в справочник
  • v8 — для файлов v8unpack.exe и zlib1.dll 

Распаковка проводилась скриптом 01_РаспаковкаФайловОбработок.cmd 

@echo off
md in out ok >nul 2>&1
cls&del log.*

echo %date% %time% #k8SjZc9Dxk*#k8SjZc9Dxk*#k8SjZc9Dxk* распаковка файлов отчетов
for %%I in (in*) do (
echo %%~nI >>log.log
v8v8unpack.exe -parse "%%I" "out\%%~nxI" >>log.log
)

Из каждого файла обработки создался каталог с распакованными внутренностями в каталоге out.

Модуль объекта ищется очень просто:

1. в каталоге обработки открывается файл root, в нем записано имя файла со свойствами обработки (в виде GUID)

root

2. в полученном файле свойств, нужно найти выражение, состоящее из GUID-а и имени объекта (обработки)

где живет нофелет

3. в каталоге, соответствующем прочитанному GUIDу с расширением .0 должен лежать файл text с текстом модуля объекта, куда мы и будем добавлять наш код для регистрации запуска обработки.

Но сначала, нужно выявить объекты (каталоги), у которых нет модуля объекта, соответственно, и каталога, и файла text. Делаем это вторым скриптом 02_ВыявлениеОбъектовБезМодуляОбъекта.cmd:

@echo off
@SETLOCAL enabledelayedexpansion

del log.absent 2>nul
echo %date% %time% #k8SjZc9Dxk*#k8SjZc9Dxk*#k8SjZc9Dxk* выявление отчетов с пустым модулем объекта
@for /D %%I in ("out*") do (
dir /b /s "%%I"|find "text">nul
if NOT !ERRORLEVEL! EQU 0 (
@echo @md "%%I">>log.absent
@echo @touch "%%I\text">>log.absent
)
)
ENDLOCAL
if exist log.absent type log.absent

Скрипт создает заготовку скрипта для создания необходимых пустых файлов text в каталогах, которые нужно выявить и прописать вручную.

Результат работы скрипта (log.absent):

@md "out00000230#Список наград.erf"
@touch "out00000230#Список наград.erf\text"

Результат ручной подстановки (make_text.cmd):

@md "out00000230#Список наград.erf19f3a161-1fd9-415b-a43a-e6313eb8df83.0"
@touch "out00000230#Список наград.erf19f3a161-1fd9-415b-a43a-e6313eb8df83.0	ext"

 

После выявления и создания недостающих файлов text, выполняется скрипт 03_ДобавлениеТекстаВМодульОбъекта.cmd, добавляющий в конец текста модуля объекта необходимый нам текст.

@echo off
@SETLOCAL enabledelayedexpansion
@Set tt=%temp%	tEXT.txt.$$$.killme
rem.>%tt% & rem.>%tt%$$$

if exist make_text.cmd call make_text.cmd
echo %date% %time% #k8SjZc9Dxk*#k8SjZc9Dxk*#k8SjZc9Dxk* добавление текста к модулю объекта...
for /R out %%K in (text) do (
find "#$#%date:~6,4%%date:~3,2%%date:~0,2%#$#" %%H >nul
if NOT !ERRORLEVEL! EQU 0 (
if exist %%~fK  echo %%K>>%tt%
)
)

type %tt%|sed "s/E:\ooo\out\.*#//;s/\[-0-9a-z]*.0\text//">%tt%$$$
oscript add2module.os %tt% %tt%$$$ addition.txt

В подкаталогах out ищется файл text, если текст этого файла не содержит ключ для исключения повторного добавления фрагмента, который выглядит как текущая дата в формате YYYYMMDD, то путь добавляется в файл %tt%, затем, из этого файла извлекаются имена обработок и записываются во второй файл. 

Для добавления текста в модуль объекта пришлось написать еще один скрипт на 1Script. Дело в том, что прикладное решение при запуске обработки из справочника ВнешниеОбработки восстанавливает файл обработки из реквизита ХранилищеВнешнейОбработки элемента справочника в файл во временном каталоге клиента с именем, совпадающим с уникальным идентификатором ссылки элемента справочника и расширением .tmp, нам такие данные для регистрации не нужны, поэтому скрипт add2module дописывает строку типа ИспользуемоеИмя = «Список наград.erf»; перед добавляемым, для регистрации программным текстом.

Скрипт add2module.os выглядит так:

Если АргументыКоманднойСтроки.Количество() <> 3 Тогда
ВызватьИсключение "требуется три параметра: список файлов, список имен обработок, файл с добавкой к модулю";
КонецЕсли;
спФайлыМодулейОбъектов  = АргументыКоманднойСтроки[0];
спИменОбработок   = АргументыКоманднойСтроки[1];
ФайлСТекстомДобавки  = АргументыКоманднойСтроки[2];

СписокМодулей = Новый ЧтениеТекста(спФайлыМодулейОбъектов, КодировкаТекста.OEM);
СписокИменОбр = Новый ЧтениеТекста(спИменОбработок, КодировкаТекста.OEM);
ФайлДобавки =  Новый ЧтениеТекста(ФайлСТекстомДобавки, КодировкаТекста.UTF8);
ТекстДобавки = ФайлДобавки.Прочитать();


ПутьДоМодуля = СписокМодулей.ПрочитатьСтроку();
ИмяОбработки = СписокИменОбр.ПрочитатьСтроку();

Пока ПутьДоМодуля <> Неопределено Цикл
//Сообщить(""+ИмяОбработки+" ### "+ПутьДоМодуля);
СтарыйТекст = Новый ЧтениеТекста(ПутьДоМодуля, КодировкаТекста.UTF8);
Стр = СтарыйТекст.Прочитать();
СтарыйТекст.Закрыть();
текстМодуля = Новый ЗаписьТекста(ПутьДоМодуля, КодировкаТекста.UTF8);

текстМодуля.ЗаписатьСтроку(Стр);
текстМодуля.ЗаписатьСтроку("");
текстМодуля.ЗаписатьСтроку("ИспользуемоеИмя = """+ИмяОбработки+""";");
текстМодуля.ЗаписатьСтроку(ТекстДобавки);
текстМодуля.Закрыть();


ПутьДоМодуля = СписокМодулей.ПрочитатьСтроку();
ИмяОбработки = СписокИменОбр.ПрочитатьСтроку();

КонецЦикла;

Полученные каталоги распакованных обработок с измененными текстами модулей объектов, собираются опять программой v8Upack.exe, но уже в каталог ok 04_СборкаИзмененныхОбработокВФайлы.cmd

@echo off
@SETLOCAL enabledelayedexpansion

echo %date% %time% #k8SjZc9Dxk*#k8SjZc9Dxk*#k8SjZc9Dxk* сборка файлов отчетов
for /D %%I in (out*) do (
echo %%~nI >>log.log
v8v8unpack.exe -build "%%I" "ok\%%~nxI" >>log.log
if NOT !ERRORLEVEL! EQU 0 echo ERROR: $$$$$$$$$$$ %%I >>log.log
)

ENDLOCAL

Затем, скриптом, ExtRepsLoad.os загружаются в справочник ВнешниеОбработки.

Update:

Для добавления вызова регистрирующей функции в модули объектов, встроенных в конфигурацию написал еще два скрипта:

Для выгрузки файлов с текстами модулей 1_unload.bat:

 

@echo off

Set cf=%~n1%
Set lg=log.log
if _%cf%_==__ echo parameter missing && @exit
md out 2>nul
del /q out*>nul

del %lg%>nul
Set fl=file.lst
Set c1exe=C:Progra~21cv8common1cestart.exe

Set stdcm=DESIGNER /s c1Server\%cf% /n adm /p pwd /Out %lg% /Visible
Set addst=/DumpConfigFiles out -Module

%c1exe% %stdcm% %addst%

Модули, традиционно складываются в каталог out.

Для изменения текстов модулей и загрузки их обратно в информационную базу 2_load.bat:

@echo off
@SETLOCAL enabledelayedexpansion

if _%1_==__ echo parametes missing && @exit
Set cf=%~n1%
Set lg=%cf%_log.log
Set fl=file.lst
Set c1exe=C:Progra~21cv8common1cestart.exe
Set stdcm=DESIGNER /Visible /s c1Server\%cf% /n adm /p pwr /Out %lg%
Set instd=/LoadConfigFiles out -Module /UpdateDBCfg -Server


rem.>%lg% && rem.>%fl%
@exit

for %%I in (outотчет.*модульобъ*.*) do @echo %%I >>%fl%
for %%I in (outобработка.*модульобъ*.*) do @echo %%I >>%fl%
echo Обработка файлов модулей отчетов и обработок
for /f %%H in (%fl%) do (
find "#$#%date:~6,4%%date:~3,2%%date:~0,2%#$#" %%H >nul
if NOT !ERRORLEVEL! EQU 0 (
echo #k8SjZc9Dxk>#k8SjZc9Dxk> %%H
type addition.txt >> %%H
)
)

echo Начинать загрузку файлов модулей в ИБ %cf%? && timeout -1
%c1exe% %stdcm% %instd%

Сначала имена файлов модулей объектов обработок и отчетов записываются в файл, затем, если текст файла не содержит ключ для исключения задвоения, в него добавляется содержимое файла addition.txt

Спасибо.

При создании статьи были использованы материалы:

1 Comment

  1. Evil Beaver

    Отличная идея, сама по себе — автоматизировать доработку модулей внешних обработок. Плюсую, однозначно!

    Но по реализации, имхо, не лучшая идея мешать «одинэсникочитаемый» код 1Script вместе с кодом BAT-скриптов. Ну а «контрольный в голову» в виде sed — это совсем негуманная жестокость )))

    Кроме того, если воспользоваться наработками проекта precommit1c, то можно на выходе получить уже человекочитаемые имена файлов метаданных, а не внутренние 1С-овские.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *