Автоматическая выгрузка данных в текстовый файл
Есть задача - организовать выгрузку данных из базы данных MarcSQL на сайт в текстовом виде (типа CSV), для поддержания информации по библиотечному фонду на сайте в актуальном состоянии.
Отступая, скажу, что у Информ-системы (разработчика программы МАРК) есть свое решение по поводу работы с базой данных книг через браузер. Называется оно "MarcSQL Internet". Стоит отдельных денег (почти 80т.р), представляет из себя запускаемые модули (.exe) для IIS сервера, что по моему скромному мнению крайне небезопасно. Если даже я и готов запустить IIS сервер в локальной сети для внутреннего пользования, то публиковать для всеобщего обозрения такую программу, с потенциальным доступом к внутреннему SQL серверу, я не готов. Есть еще один путь выгрузки файла на сайт - создать скрипт прямой выборки данных из SQL базы данных, однако это слишком сложно делать с нуля. Давать доступ разработчикам сайта к SQL серверу также небезопасно. Поэтому было решено раз в сутки передавать выборку из БД в текстовом виде на сайт, созданный в самой программе MARC-SQL.
Шаг первый: выгрузка.
Для выгрузки информации из БД Марк-а в текстовом виде, в программе есть соответствующее меню (чтобы меню стало активным, надо вначале сделать поиск книг по БД):
Для выбора полей экспорта, в каталоге программы есть файл Tools.ini. Вот как он выглядит у меня:
[TxtExport]
Tags=100a,245a,245b,245c,090x,090a,773t,773d,773g,260a,260b,260c,300a,020a,520a,653a
FirstLine=Автор|Заглавие|Продолжение заглавия|Ответственность|Авторский знак|Полочный индекс|Название источника|Место и дата издания|Прочая информация|Место издания|Издательство|Дата издания|Объем|ISBN|Аннотация|Ключевые слова
ColunmSeparator=|
TagSeparator=,
Trim=YES
Соответственно такой формат данных и будет представлен в экспортируемом текстовом файле.
Все работает, все обновляется. Но есть одна проблема: программа категорически не поддерживает выгрузку текстовых данных из командной строки. После переписки с разработчиками программы, стало ясно что этой функции не было, нет и не будет в будущем.
Было решено автоматизировать процесс самостоятельно. Нужна программа, которая вместо пользователя бы запускала программу, нажимала на менюшки и выгружала результат на сайт. Были пересмотрены утилиты автоматизации, наподобие AutoIt!, но в силу ограниченного времени, и желания упростить процесс, я решил использовать встроенный язык VBScript для генераций комбинаций клавиш для поиска книг, переходов по меню программы и выгрузки файла экспорта. Мои потуги можно почитать тут.
Скрипт send.vbs на VBScript для нажатий клавиш в программе выглядит примерно так:
Set WshShell = CreateObject("WScript.Shell")
Set WshExec = WshShell.Exec("C:\путь_к_файлу\marcp.exe")
WScript.Sleep 10000
WshShell.AppActivate(WshExec.ProcessID)
WSHShell.SendKeys "+{F7}"
WScript.Sleep 5000
WSHShell.SendKeys "%"
WSHShell.SendKeys "{DOWN}{LEFT}{LEFT}{LEFT}{LEFT}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}{ENTER}"
WScript.Sleep 2000
WSHShell.SendKeys "c:\путь_к_файлу\export.txt{ENTER}Y"
WScript.Sleep 100000
WSHShell.SendKeys "{ENTER}%{F4}"
Соответственно строки наподобие WScript.Sleep 100000 (задержка на 100 сек) являются временными задержками, и подобраны экспериментальным путем. В вашем случае придется тоже подбирать значения, исходя из пропускной способности сети, мощности сервера, объема базы данных и т.п.
Для упрощения скрипта я сделал выгрузку из всех баз в один файл export.txt, который после закрытия программы будет скопирован в нужную папку с нужным названием (например base1.txt, base2.txt и т.д.)
Так как баз данных с книгами в библиотеке несколько, то соответственно надо выгружать эти базы по-отдельности. Проблема в том, что в интерфейсе программы базы можно выбирать только мышкой, и никакие комбинации клавиш не позволяют добраться до этого меню. Было решено запускать МАРК несколько раз с разными файлами настроек подключения к базе данных.
Настройки подключения к БД, у программы МАРК, хранятся в файле dsn.ini. Вот пример этого файла:
текстовое_описание_базы1|DRIVER=SQL Server;UID=имя_пользователя;PWD=пароль_пользователя;Address=адрес_SQL_сервера,1433;Network=DBMSSOCN;DATABASE=имя_базы1;WSID=Administrator;SERVER=адрес_SQL_сервера
текстовое_описание_базы2|DRIVER=SQL Server;UID=имя_пользователя;PWD=пароль_пользователя;Address=адрес_SQL_сервера,1433;Network=DBMSSOCN;DATABASE=имя_базы2;WSID=Administrator;SERVER=адрес_SQL_сервера
текстовое_описание_базы3|DRIVER=SQL Server;UID=имя_пользователя;PWD=пароль_пользователя;Address=адрес_SQL_сервера,1433;Network=DBMSSOCN;DATABASE=имя_базы3;WSID=Administrator;SERVER=адрес_SQL_сервера
текстовое_описание_базы4|DRIVER=SQL Server;UID=имя_пользователя;PWD=пароль_пользователя;Address=адрес_SQL_сервера,1433;Network=DBMSSOCN;DATABASE=имя_базы4;WSID=Administrator;SERVER=адрес_SQL_сервера
текстовое_описание_базы5|DRIVER=SQL Server;UID=имя_пользователя;PWD=пароль_пользователя;Address=адрес_SQL_сервера,1433;Network=DBMSSOCN;DATABASE=имя_базы5;WSID=Administrator;SERVER=адрес_SQL_сервера
Соответственно делаем 4 файла dsn.ini под разными названиями, и с одним подключением в каждом файле, и перед каждым запуском программы копировать файл настроек в папку программы. Вот пример этого скрипта (первой строкой стоит закрытие всех инстанций программы marcp.exe, которые висят в памяти):
taskkill /IM markp.exe
copy dsn.ini.base1 dsn.ini
marcp.exe
copy export.txt done\base1.txt
copy dsn.ini.base2 dsn.ini
marcp.exe
copy export.txt done\base2.txt
copy dsn.ini.base3 dsn.ini
marcp.exe
copy export.txt done\base3.txt
copy dsn.ini.base4 dsn.ini
marcp.exe
copy export.txt done\base4.txt
copy dsn.ini.base5 dsn.ini
marcp.exe
copy export.txt done\base5.txt
После запуска скрипта (назовем его для примера start.cmd), произойдет запуск программы, выгрузка базы в текстовый файл, копирование файла в папку done с новым названием, и далее по кругу, пока все базы не выгрузятся. В итоге в папке done будут лежать файлы base1.txt, base2.txt, base3.txt, base4.txt, base5.txt.
Шаг второй: автоматизация выгрузки на сайт.
После получения файлов, надо их заархивировать и выгрузить на сайт через FTP. Можно конечно использовать утилиты, наподобие 7zip, pkzip, rar и т.п, и т.д., однако это не наш метод. Ведь в Windows XP и старше, есть возможность архивировать файлы средствами самого Windows. Правда есть одна загвоздка - нет такой команды, которая заставит винду провести архивацию файлов. И снова на помощь приходит VBScript. Подробно про мои потуги можно почитать тут. Соответственно берем файл zip.vbs и делаем вызов скрипта через запуск:
cscript zip.vbs c:\путь_к_файлам\done\ c:\пусть_экспорта\все_базы.zip
После этого надо выложить файл на FTP сайта. Вот тут стандартные средства Windows становятся крайне неудобными. Поэтому воспользуемся Freeware утилитами от Linux, работающие под Windows.
Для использования Wput.exe применяем такую команду:
wput --append-output=upload.log -u -B "c:\путь_экспорта\все_базы.zip" "ftp://логин:пароль@адрес_сайта/все_базы.zip"
После запуска команды произойдет закачка архива на сайт через FTP, и создание файла upload.log с логом работы утилиты (может помочь отследить работу скрипта).
Шаг третий: автоматизация запуска скрипта.
Теперь надо сделать так, чтобы наш скрипт запускался по расписанию на сервере, и работал без нашего участия.
Ошибки/проблемы/траблы:
Если вбить в планировщик заданий запуск нашего скрипта, то ничего не произойдет.
Если создать новую учетную запись в системе (например ScripTasks), дать нужные права, запускать запуск скрипта от имени этого пользователя, то ничего не произойдет.
Если учетной записи службы планировщика заданий разрешить интерактивное взаимодействие в рабочим столом, то максимум это заработает, только если наш пользователь ScripTasks зашел в систему (а в windows 7 это вообще запрещено для безопасности).
Тут появляется два пути решения проблемы для автоматизации процесса выгрузки:
сделать на сервере автологин нужной учетной записи по паролю, дать службе планировщика заданий права взаимодействия с рабочим столом, и затем не трогать сервер и не давать доступ к нему никому, т.к. это может нарушить выгрузку данных. Плюс любой сможет выключить сервер/скачать данные/стереть данные несколькими щелчками мыши, даже не зная пароля, т.к. рабочий стол открыт. Как включить автологон можно глянуть тут.
дать нашей учетке права входить на сервер через RDP (удаленный рабочий стол) по логину/паролю, и выполнение всех операций в удаленном рабочем столе, без опасности вмешательства уборщицы/пользователя/админа в процесс.
Выбираем сложный второй метод.
Чтобы этот метод заработал:
создаем учетную запись ScripTasks, даем ей нужные права, достаточные для запуска Marc-SQL
добавляем учетку в группу "Пользователи удаленного рабочего стола"
создаем .RDP файл с логином/паролем для скрипта (подробности моих мучений смотрите тут).
прописываем в файле connect.rdp автозапуск нашего скрипта выгрузки данных в поле alternate shell.
скрипт захода на рабочий стол запускается командой mstsc.exe connect.rdp
заходим для проверки пару раз под этой учеткой на удаленный рабочий стол (в нашем случае адрес сервера будет локальный 127.0.0.1), чтобы убрать лишние менюшки и вопросы разных программ, и запускаем для проверки скрипт - он должен работать
делаем новую задачу в планировщике заданий на запуск скрипта в выбранное время под учеткой ScripTasks и ее паролем.
запускаем задачу, смотрим результат. Выставляем таймаут закрытия задачи в 1 час.
проверяем, что все работает. В диспетчере заданий делаем отображение кода сеанса, чтобы отследить запуск скрипта в RDP подключении.
Внимание!
Не забываем, что приведенный выше код представляет из себя т.н. "рыбу", в которой надо изменять названия файлов на английские для целей совместимости, изменять пути запуска программ, вбивать логины/пароли к базам данных и т.п. В общем процесс творческий, и предполагает работу головой. Также необходимо ознакомиться с информацией на родственных страницах, которые я привожу в качестве примера для того, чтобы понимать что к чему. Базовые скрипты приведены в аттаче export.7z.