Автоматизируем FTP с помощью Python::Журнал СА 12.2004
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Архив номеров
Контакты
   

  Опросы
  Статьи

Электронный документооборот  

5 способов повысить безопасность электронной подписи

Область применения технологий электронной подписи с каждым годом расширяется. Все больше задач

 Читать далее...

Рынок труда  

Системные администраторы по-прежнему востребованы и незаменимы

Системные администраторы, практически, есть везде. Порой их не видно и не слышно,

 Читать далее...

Учебные центры  

Карьерные мечты нужно воплощать! А мы поможем

Школа Bell Integrator открывает свои двери для всех, кто хочет освоить перспективную

 Читать далее...

Гость номера  

Дмитрий Галов: «Нельзя сказать, что люди становятся доверчивее, скорее эволюционирует ландшафт киберугроз»

Использование мобильных устройств растет. А вместе с ними быстро растет количество мобильных

 Читать далее...

Прошу слова  

Твердая рука в бархатной перчатке: принципы soft skills

Лауреат Нобелевской премии, специалист по рынку труда, профессор Лондонской школы экономики Кристофер

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 9931
Комментарии: 0
Потоковая обработка данных

 Читать далее...

19.03.2018г.
Просмотров: 8141
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

 Читать далее...

19.03.2018г.
Просмотров: 8247
Комментарии: 0
Конкурентное программирование на SCALA

 Читать далее...

19.03.2018г.
Просмотров: 5222
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 5906
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

Друзья сайта  

 Автоматизируем FTP с помощью Python

Архив номеров / 2004 / Выпуск №12 (25) / Автоматизируем FTP с помощью Python

Рубрика: Программирование /  Автоматизация   | Дополнительные материалы

СЕРГЕЙ СУПРУНОВ

Автоматизируем FTP с помощью Python

Любой системный администратор, если он в меру ленив, рано или поздно берется за автоматизацию своей работы. Сначала вместо длинных команд со множеством ключей появляются псевдонимы (aliases), потом группы команд объединяются в пакетные файлы командной оболочки, затем на арену выходят сценарии на более функциональных языках типа Perl… И все для того, чтобы сократить количество «кликов» и по возможности переложить часть работы на пользователей, которые эти операции и должны бы делать «по идее», но «по факту» научить их находить нужный файл в дереве каталогов, упаковывать его и отправлять по назначению оказывается на порядок сложнее, чем делать это самому.

Об использовании Python для выполнения тех или иных операций на UNIX-серверах написано достаточно много. В данной же статье я хочу показать применение этого языка для решения «бытовых» задач в среде Windows. В качестве примера (которым возможности Python никоим образом не ограничиваются) разработаем приложение, автоматизирующее отправку данных и получение обновлений по FTP, используя заранее настроенные пути и имена файлов.

Попытаемся сделать наше приложение максимально универсальным, однако будем иметь в виду конкретную цель – нужно организовать обмен данными с удаленной кассой (ежедневно сбрасывать на сервер файлы реестров и время от времени получать с сервера обновленную базу абонентов). Все задачи условно разделим на пользовательские (например, инициирование отправки) и администраторские (например, первичная настройка). Первые из них требуется разработать максимально удобно, предоставив простейший интерфейс и лишив пользователя возможности ошибаться. Задачи второй категории по традиции решим достаточно упрощенно – главное, чтобы все работало при минимуме телодвижений.

Первым делом скачаем с http://python.org дистрибутив Python для Windows. Его инсталляция никаких вопросов вызвать не должна. На момент написания статьи у меня была установлена версия Python 2.3.4. Входящая в состав пакета среда разработки IDLE (рис. 1) предоставляет некоторые удобства, но для меня как-то уже устоялся стиль разработки, когда код вбивается в обычный текстовый файл, и затем исполняется командой типа:

C:myworkspython est>python test.py

Можно, конечно, в командной строке набрать просто test.py – в процессе инсталляции в Windows расширение «.py» ассоциируется с интерпретатором python.exe, и подобная команда тоже выполнит код. Однако на стадии отладки это не совсем удобно – для потоков stdout и stderr в этом случае создается новое консольное окно, которое закрывается сразу же после завершения (в том числе и аварийного) работы сценария, не позволяя ознакомиться с сообщениями об ошибке.

Рисунок 1

Рисунок 1

Знакомство с Python

Подробно останавливаться на основах языка я не буду. Данный раздел имеет целью коротко пояснить, что и как, для тех, кто ранее с Python не сталкивался, но статью прочитать непременно хочет. И сразу, как говорится, – с места в карьер:

(0) # -*- coding: cp866 -*-

(1) #----------------------------- first.py

(2) import os

(3) def hello(message):

(4)        print message

(5)        print 'Вы находитесь в %s.' % os.getcwd()

(6)

(7) if __name__ == '__main__':

(8)        hello('Hello from Python.')

(9)        raw_input('Нажмите Enter...')

(a) else: pass

(b) #----------------------------end of first.py

Результат работы этого сценария будет следующим:

C:myworkspython est>python test.py

Hello from Python.

Вы находитесь в C:myworkspython est.

Нажмите Enter...

(0) – первая строка указывает интерпретатору, в какой кодировке следует рассматривать приведенный ниже текст. Для ASCII-текста она не требуется, но поскольку мы используем кириллицу, то без нее интерпретатор будет каждый раз выдавать предупреждение «DeprecationWarining: Non-ASCII character…».

(1, b) – комментарии, как и принято в UNIX, предваряются символом «#». Для многострочных комментариев часто используют обрамление текста с помощью утроенных кавычек: «’’’ multiline comment ’’’». Этот же прием можно использовать для временного исключения участков кода на стадии отладки.

(2) – третья строка импортирует модуль, в данном случае «os», который является стандартным для Python и содержит функции для работы с конкретной операционной системой. В нашем примере из этого модуля мы используем функцию getcwd(), возвращающую текущий каталог.

(3) – с этой строки начинается определение функции «hello». В скобках указывается список параметров, причем даже если параметров нет, скобки обязательно должны быть. Двоеточие начинает блок кода, содержащий тело функции. В отличие от таких языков, как C или Perl, в Python блок операторов (определения функций, циклы, ветвления) не обрамляется операторными скобками, а выделяется отступом. Отступ может быть выполнен любым количеством символов «пробел», но все операторы блока должны иметь одинаковый отступ. Первая же строка, выполненная без отступа, рассматривается как окончание блока операторов.

Здесь хочется сделать отступление и немного пофилософствовать о стиле программирования. Тот же Perl позволяет кодировать как угодно – хоть в одну строку. С одной стороны, каждый программист со временем вырабатывает тот стиль, который ему более удобен. При переходе на Perl с других языков можно перенести и устоявшийся стиль написания кода. Но с другой стороны тут есть и вероятность, что кто-то другой будет очень долго воспроизводить «не-ASCII»-звуки, пытаясь разобраться в исходниках программы, написанной как-нибудь экзотически. Язык Python в этом смысле более требователен к разработчику, унифицируя тем самым стиль кодирования и упрощая работу тем, кому придется работать с этим кодом в будущем. Однако продолжим…

(4, 5) – оператор «print» выводит на экран значение строковой переменной или непосредственно строку, заключенную в кавычки или апострофы. Вставка в строку переменных выполняется в стиле оператора printf языка Си, за тем исключением, что список интегрируемых переменных задается после символа «%» как кортеж (то есть через запятую и в круглых скобках). Если переменная одна, как в нашем случае, скобки можно опустить.

(6) – пустые строки, естественно, допускаются.

(7, a) – оператор ветвления пояснять, думаю, не нужно. Как и обычно, задаются условие и блок операторов (выделяется отступом), которые будут выполнены, если условие истинно. В противном случае, если задан далее оператор «else», будет выполнен его блок операторов. Если команда в блоке одна, допускается указывать ее непосредственно после двоеточия в этой же строке (хотя это и противоречит официальному стилю программирования). Оператор «pass» ничего не делает, и строго говоря, строка «a» в нашем случае совершенно не нужна.

Чуть подробнее следует остановиться на самом условии, указанном в строке (7): __name__== ‘__main__’. Встроенная переменная __name__ возвращает имя программы, если вызывается как модуль из другого сценария (об этом – чуть ниже) либо строку «__main__», если файл запускается непосредственно. То есть код строк «8-9» будет выполнен только тогда, когда файл first.py запускается из командной строки. Зачем это сделано, будет объяснено далее.

(8) – здесь мы просто вызываем описанную ранее функцию «hello», передав ей текстовую строку в качестве аргумента.

(9) – функция raw_input считывает со стандартного потока ввода stdin строку, завершающуюся символом перевода строки. В данном случае этот оператор используется исключительно для задержки вывода, чтобы консольное окно не закрывалось сразу.

Теперь рассмотрим еще одну концепцию Python – повторное использование кода. Конечно, это позволяет делать любой язык программирования, но в Python повторное использование положено в основу синтаксиса, и все получается очень легко и естественно.

Для примера напишем еще один сценарий:

(0) import first

(1) first.hello("Hi")

Запустив его, мы получим такой вывод:

C:myworkspython est>python test.py

Hi.

Вы находитесь в C:myworkspython est.

То есть мы подключили нашу первую программу как модуль и получили возможность использовать определенные в ней функции. Код вне функций (в нашем случае это строки «0, 1, 6-b») выполняется в момент импорта. Именно для того, чтобы избежать какой-либо активности нашего первого сценария во время его импортирования, мы и ввели проверку имени __name__, чтобы определить, подключается наш сценарий к другому или запускается автономно.

Говоря об импорте, следует указать еще на один синтаксис подключения: «from first import hello». Эта команда импортирует из first.py только функцию hello. Через запятую можно перечислить и несколько импортируемых функций. Команда «from first import * » импортирует в текущий сценарий все функции, найденные в first.py. Отличие этого синтаксиса от использованного в second.py заключается в том, что имена функций включаются в пространство имен импортирующего сценария, и поэтому подгруженные функции должны вызываться без указания модуля:

(0) from first import hello

(1) hello("Hi")

Ну и для полноты картины – несколько слов о библиотеке графических объектов Tkinter. Основанная на Tk/Tcl, она позволяет достаточно просто наделять сценарии графическим интерфейсом:

(0) # -*- coding: cp866 -*-

(1) from Tkinter import *

(2)

(3) window = Tk()

(4) window.title(u"Простое графическое окно")

(5) label = Label(window, text = u" Простая текстовая метка")

(6) label.config(fg = "blue", font = ("Georgia", 14, "italic"))

(7) label.pack()

(8) button = Button(window, text = u"Закрыть", command = window.quit)

(a) button.pack(expand=YES, fill=X)

(b) window.mainloop()

В строке «1» импортируем все из модуля Tkinter. Далее последовательность действий такова: объявляем окно window верхнего уровня (3); задаем ему текст заголовка (4); описываем и упаковываем текстовую метку (5-7); аналогично поступаем с кнопкой (8-a) и, наконец, в строке «b» запускаем на выполнение цикл окна.

Синтаксис описания различных элементов во многом одинаков – первым параметром задается родительский элемент (в данном случае окно верхнего уровня), к которому прикрепляется данный элемент. Далее перечисляются другие параметры: «text» для задания отображаемого текста, «command» определяет обработчик обратного вызова, который будет исполнен при выборе данного элемента (в данном примере при нажатии на кнопку главное окно будет закрыто). Параметры можно задавать и с помощью метода «config» графического объекта, как показано в строке «6».

Любой объект должен быть размещен одним из менеджеров размещения (в данном примере используем наиболее простой из них – pack). То есть для каждого объекта требуется вызвать метод pack(), который поместит его в конкретном месте окна. С помощью дополнительных параметров можно управлять упаковкой, например, указывать, к какой стороне окна требуется прикрепить элемент (параметр side), следует ли растягивать элемент при изменении размеров окна (expand), должен ли элемент заполнять все отведенное ему пространство в указанном направлении (fill). Самый лучший способ познакомиться с особенностями упаковки – на практике попробовать различные варианты.

Метод mainloop() запускает цикл обработки событий графического окна. При его вызове окно с упакованными на нем элементами отображается на экране, и дальнейшее управление поведением сценария возможно только с помощью обработчиков обратных вызовов, которым передается управление при наступлении того или иного события. Метод quit, строго говоря, осуществляет выход из цикла mainloop и передачу управления на следующий оператор сценария.

Обратите внимание на символ «u» перед текстовыми строками. Этот хитрый оператор заставляет Python преобразовывать следующую далее текстовую строку в кодировку Unicode. Базовая кодировка должна быть задана так, как показано в строке «0». В версиях Python до 2.3 этот синтаксис не действует, и там требуется явно задавать функцию преобразования unicode().

Еще одно замечание – внутри скобок допускается перенос строки и произвольный отступ перенесенной части, как это продемонстрировано в строках «8-9».

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

Рисунок 2

Рисунок 2

Модуль для архивирования myzip

Поскольку передавать по сети файлы в запакованном виде намного приятнее, особенно когда речь идет о коммутируемой линии, то заготовим несколько функций для работы с zip-файлами. Чтобы иметь возможность использовать эти функции и в других приложениях, поместим их в отдельный файл:

# -*- coding: cp866 -*-

#--------------------------------------------------------

#

# myzip.py: модуль работы с zip-архивами

#

#--------------------------------------------------------

# Модуль для работы с zip-архивами

from zipfile import *

# Импортируется функция glob для обхода каталогов

from glob import glob

# Если не ноль – выводить сообщения на экран

VERBOSE = 1

# Очистка архива от имеющихся в нем файлов: архив открывается на запись и сразу закрывается

def clearzip(zipname):

    if VERBOSE:

       print 'myzip: Очистка архива %s.' % zipname

    zip = ZipFile(zipname, 'w', ZIP_DEFLATED)

    zip.close

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

def writezip(zipname, filename):

    if VERBOSE:

       print 'myzip: Упаковывается %s в %s.' % (filename, zipname)

    zip = ZipFile(zipname, 'a', ZIP_DEFLATED)

    zip.write(filename)

    zip.close

# Запись в архив всех файлов из каталога, удовлетворяющих шаблону

def writepattzip(zipname, patterns):

    if VERBOSE:

       print 'myzip: Создается архив %s...' % zipname

    clearzip(zipname)

    for pattern in patterns:

           filelist = glob(pattern)

           for filename in filelist:

                 writezip(zipname, filename)

    if VERBOSE:

       print 'myzip: Архив создан.'

# Извлечение файла из архива

def readzip(zipname, filename):

    if VERBOSE:

       print 'myzip: Извлекается %s из %s.' % (filename, zipname)

    zip = ZipFile(zipname, 'r', ZIP_DEFLATED)

    open(filename, 'wb').write(zip.read(filename))

    zip.close

# Извлечение всех файлов из архива

def readallzip(zipname):

    if VERBOSE:

       print 'myzip: Читается архив %s...' % zipname

    zip = ZipFile(zipname, 'r', ZIP_DEFLATED)

    for filename in zip.namelist():

           readzip(zipname, filename)

    zip.close

    if VERBOSE:

       print 'myzip: Архив прочтен.'

# Если запускается из командной строки, то можно что-то сделать

if __name__ == '__main__': print 'Только как модуль.'

В основе данного сценария лежит стандартный модуль zipfile. Он предоставляет довольно развитые средства для работы с архивами, но его непосредственное использование потребует каждый раз писать один и тот же код обвязки. Поэтому мы и создаем собственный модуль, в котором опишем нужные нам функции на более высоком уровне.

Класс ZipFile модуля zipfile позволяет работать с архивом, почти как с обычным файлом – читать из него, записывать и т. д. Фигурирующий здесь параметр ZIP_DEFLATED определяет метод компрессии.

В дальнейшем нам понадобятся отсюда: функция read-allzip, которая будет извлекать в текущую папку все файлы из скачанного архива, и writepattzip, которая будет упаковывать в архив все файлы из текущего каталога. Функции соответствуют шаблону.

Из интересного отметим здесь, что шаблоны передаются в виде кортежа, то есть этот параметр должен выглядеть примерно так: «(‘*.db’, ‘*.px’)». Также обратите внимание на синтаксис цикла «for»: он перебирает все значения из списка, заданного после ключевого слова «in».

Модуль работы с FTP myftp

Аналогично в отдельный файл вынесем и функции работы по протоколу FTP. Здесь нам требуется две функции – отправка одного файла по заданному пути и чтение одного файла. Возможность загрузки или отправки сразу нескольких файлов реализовывать не будем: поскольку в основе нашего приложения будет лежать работа с архивами, то целесообразно упаковывать все передаваемое или принимаемое в один файл. Код модуля представлен ниже:

# -*- coding: cp866 -*-

#--------------------------------------------------------

#

# myftp.py: модуль работы с FTP

#

#--------------------------------------------------------

# Импорт нужных функций

import os

from ftplib import FTP

from myzip import *

VERBOSE = 1

# Скачивание файла по FTP:

# передаются следующие параметры:

# fromdir – папка-источник на FTP-сервере

# file – имя скачиваемого файла

# (fromsite, ftpuser, ftppassword) – FTP-сервер и логин/пароль для входа

# todir – локальная папка для сохранения скачанного файла

#

def getftp(fromdir, file, (fromsite, ftpuser, ftppassword), todir):

    olddir = os.getcwd()       # запоминается текущий каталог

    os.chdir(todir)            # переход в папку назначения

    if VERBOSE:

       print 'myftp: Устанавливается соединение с %s...' % fromsite

    try:

           localfile = open(file, 'wb')

           connection = FTP(fromsite)

           connection.login(ftpuser, ftppassword)

           connection.cwd(fromdir)

    except:

           print 'myftp: ОШИБКА СОЕДИНЕНИЯ С %s!' % fromsite

           return(-1)

    if VERBOSE:

       print 'myftp: Соединение с %s установлено.' % fromsite

    if VERBOSE:

       print 'myftp: Выполняется загрузка %s...' % file

    try:

           connection.retrbinary('RETR ' + file, localfile.write, 1024)

           connection.quit()

           localfile.close()

    except:

           print 'myftp: ОШИБКА ПОЛУЧЕНИЯ ФАЙЛА %s' % file

           return(-2)

    if VERBOSE:

       print 'myftp: Загрузка файла %s завершена.' % file

 

    # используем try, поскольку скачанный файл может оказаться не zip-архивом.

    # Хотя это можно было бы проверить и явно:

    try:

       readallzip(file)

    except:

       print 'myftp: %s не является архивом, помещен как есть.' % file

    os.chdir(olddir)    # возвращаемся в прежний каталог

# Загрузка файла по FTP на сервер:

# передаются следующие параметры:

# fromdir – локальная папка-источник

# file – имя отправляемого zip-файла

# pattern – список шаблонов для отправки

# (fromsite, ftpuser, ftppassword) – FTP-сервер и логин/пароль для входа

# todir – папка на FTP-сервере для загрузки файла

#

def putftp(fromdir, zipfile, pattern, (tosite, ftpuser, ftppassword), todir):

    olddir = os.getcwd()

    os.chdir(fromdir)

    if VERBOSE:

       print 'myftp: Подготовка файлов к отправке...'

    writepattzip(zipfile, pattern)

    if VERBOSE:

       print 'myftp: Файлы упакованы в %s.' % zipfile

           print 'myftp: Устанавливается соединение с %s...' % tosite

    try:

           localfile = open(zipfile, 'rb')

           connection = FTP(tosite)

           connection.login(ftpuser, ftppassword)

           connection.cwd(todir)

    except:

           print 'myftp: ОШИБКА СОЕДИНЕНИЯ С %s' % tosite

           return(-2)

    if VERBOSE:

       print 'myftp: Соединение с %s установлено' % tosite

           print 'myftp: Выполняется отправка файла %s...' % zipfile

    try:

           connection.storbinary('STOR ' + zipfile, localfile, 1024)

           connection.quit()

           localfile.close()

    except:     

           print 'myftp: ОШИБКА ОТПРАВКИ ФАЙЛА %s!' % zipfile

           return(-2)

    if VERBOSE:

       print 'myftp: Файл %s отправлен.' % zipfile

    os.chdir(olddir)

Здесь следует обратить внимание на то, что файлы следует открывать в бинарном режиме (используем параметры «rb» и «wb»), чтобы они не пострадали в результате интерпретации Python символов конца строки.

Всю черную работу в этом сценарии выполняет стандартный модуль ftplib, а конкретно – его класс FTP, который отвечает за методы установления соединения, обеспечивает работу протокола FTP и т. д.

Потенциально опасные операции, которые могут вызвать ошибку (например, FTP-сервер может быть недоступен), заключены в оператор «try – except». Он в свою очередь перехватывает ошибку и не позволяет нашему приложению «обрушиться» со страшными для пользователя ругательствами.

Файл конфигурации config.py

Конечно, нужные нам переменные, такие как URL FTP-сервера, имя и пароль пользователя, и прочее, можно задать непосредственно в главном модуле нашего приложения, но несколько удобнее представляется вынос этих описаний в отдельный файл. Чтобы не связываться с разбором конфигурационного файла и не изобретать ему какой-то формат, будем использовать в его качестве обычный сценарий Python, в котором определяются соответствующие переменные. Затем, будучи импортирован в главный сценарий, он предоставит свои переменные другим функциям. В конце концов это уже относится к «административным» функциям, а мы условились не тратить много усилий на красоту в данном случае.

# -*- coding: cp866 -*-

#------------------------------------------------------

# предвычисляемый код:

import os

from glob import glob

olddir = os.getcwd()

root = 'data/'

os.chdir(root)

list = glob('Pay_*')

list.sort()

srcdir = root + list[-1]       # последний элемент списка

os.chdir(olddir)

# print 'config: Текущий каталог отправки – %s' % srcdir

#-----------------------------------------------------

# Список FTP-серверов. Формат записи:

# (FTP-сервер, логин, пароль)

ftpservers = [

('ftp.my.server.ru', 'user', 'password'),

('ftp.freebsd.org', 'ftp', 'my@mail.ru'),

]

#-----------------------------------------------------

# Список операций. формат записи:

# (номер, тип операции, название операции, номер FTP-сервера, папка-источник, имя архива, шаблон, папка назначения)

operations = [

(0, 'get', u'Обновить данные', 0, 'downdata', 'basa.zip', ('*',), 'data'),

(1, 'put', u'Отправить реестры', 0, srcdir, 'reestr.zip', ('*.db', '*.px',), 'updata'),

(2, 'get', u'Обновить программу', 0, 'ftpman', 'ftpman.zip', ('*',), 'bin'),

(3, 'get', u'Получить README.TXT', 1, 'pub/FreeBSD', 'README.TXT', ('*',), '.'),

Наиболее важными здесь являются список FTP-серверов и список операций. Код в начале сценария, помеченный как «предвычисляемый», нужен для динамического определения значения некоторых переменных, подставляемых в списки. В нашем примере в нем вычисляется значение переменной srcdir, которая задает папку-источник данных, используемую в операции 1 («Отправить реестры»). В качестве источника должна использоваться папка с именем вида Pay_NN, где NN – двузначное число, с наибольшим значением NN. Для того чтобы определить эту папку на данный момент, и нужен этот «предвычисляемый» код.

Список FTP-серверов задается как список кортежей, каждый из которых содержит адрес сервера, имя и пароль пользователя для доступа на этот сервер. В дальнейшем обращение к данным по тому или иному серверу выполняется по порядковому номеру записи (начиная с нуля). Например, имя пользователя, которое должно использоваться для доступа на ftp.freebsd.org, можно будет получить так: «ftpservers[1][1]».

Аналогично задается список операций. Назначения полей приведены в комментарии. Номер операции здесь указываем явно – так будет удобнее в дальнейшем. Второе поле задает саму операцию – получение данных (get) или отправка (put).

Конечно, формат нашего конфигурационного файла способен напугать кого угодно, однако пользователям не придется с ним работать, а администратор как-нибудь разберется.

Главный сценарий manager.py

Теперь попытаемся объединить все это в законченное приложение:

# -*- coding: cp866 -*-

from Tkinter import *

from myftp import getftp, putftp

VERBOSE = 1

if VERBOSE: print '================> manager.py запущен.'

# Служебно-декоративная функция. Нужна, чтобы менять текст метки в зависимости от состояния

def chStat(messvar, color, btn, btnrelief):

    # изменяем границу кнопки так, чтобы в момент выполнения операции она выглядела вдавленной –

    # исключительно «декоративный» эффект

    btn.config(relief = btnrelief)

    # меняем текст метки

    status.config(text = messvar)

    # меняем цвет метки

    status.config(fg = color)

    # перерисовываем метку

    status.update()

# Функция получения данных. Назначается как обработчик кнопкам операций типа 'get'

def get(nr):

    if VERBOSE: print '================> выполняется загрузка...'

    # выбираем нужный кортеж

    op = config.operations[nr]

    # вычисляем имя кнопки по номеру

    exec 'obj = btn%d' % op[0]

    # выбираем данные по FTP-серверу

    ftpsite = config.ftpservers[op[3]]

    msg =  u'Подождите: выполняется загрузка...'

    chStat(msg, '#AA0000', obj, 'sunken')

    err = getftp(op[4], op[5], ftpsite, op[7])

    if err:

           msg = u'ОШИБКА ПОЛУЧЕНИЯ ДАННЫХ!.'

           chStat(msg, '#FF0000', obj, 'raised')

    else:

           msg = u'Данные получены.'

           chStat(msg, '#007700', obj, 'raised')

    if VERBOSE: print '================> загрузка завершена.'

# Функция отправки данных. Назначается как обработчик кнопкам операций типа 'put'

def put(nr):

    if VERBOSE: print '================> выполняется отправка...'

    op = config.operations[nr]

    exec 'obj = btn%d' % op[0]

    ftpsite = config.ftpservers[op[3]]

    msg =  u'Подождите: выполняется отправка...'

    chStat(msg, '#AA0000', obj, 'sunken')

    err = putftp(op[4], op[5], op[6], ftpsite, op[7])

    if err:

           msg = u'ОШИБКА ОТПРАВКИ ДАННЫХ!'

           chStat(msg, '#FF0000', obj, 'raised')

    else:

           msg = u'Данные отправлены.'

           chStat(msg, '#007700', obj, 'raised')

    if VERBOSE: print '================> отправка выполнена.'

# Создаем главное окно

tk = Tk()

tk.title(u'Центр управления обменом')

# Некоторые декоративные элементы...

topf = Frame(tk)

topf.pack(expand=NO, fill=X)

lbl = Label(topf, text = u'  – Автоматизация обмена данными по FTP -   ')

lbl.config(font = ('Georgia', 9, 'italic bold'))

lbl.config(bg = '#AAAAFF')

lbl.pack(side=TOP, expand=YES, fill=X)

# Создаем статус-метку, в которую будет выводиться информация о состоянии

midf = Frame(tk)

midf.pack(expand=YES, fill=BOTH)

status = Label(midf, text = u'Подробный вывод см. в окне консоли')

status.config(bg='#FFFFFF', bd=2, relief=SUNKEN, height=3)

status.pack(expand=YES, fill=BOTH)

# Фрейм для размещения кнопок операций. Нужен для управления растягиванием при изменении размеров окна – весь фрейм

# будет «привязан» к нижней кромке окна

comf = Frame(tk)

comf.pack(fill=X)

# Импортируем конфигурационный файл.

# Именно в этот момент будут выполнены «предвычисления»

import config

# Перебирая все кортежи в списке операций, динамически создаем для каждой из них кнопку

for op in config.operations:

    cmd = "btn%d = Button(comf, text = '%s', " % (op[0], op[2])

    cmd = cmd + "command = (lambda: %s(%d))) " % (op[1], op[0])

    exec cmd

    exec 'btn%d.pack(fill=X)' % op[0]

# Кнопка «Закрыть» и еще немного декораций

closeBtn = Button(comf, text = u'Закрыть', command = tk.quit)

closeBtn.pack(side=RIGHT)

copyLbl = Label(comf, text = u'Coded by Amsand, 2004')

copyLbl.config(font = ('Georgia', 8, 'italic'))

copyLbl.pack(side=LEFT)

# Выводим на экран полученное окно и передаем ему управление

mainloop()

# Когда окно будет закрыто, управление вновь вернется сценарию

if VERBOSE: print '================> manager.py остановлен.'

В результате исполнения этого сценария на экране отобразится графическое окно наподобие изображенного на рис. 3. Одновременно с этим будет открыто консольное окно, куда будет направляться вывод операторов print (рис. 4). Отдавая сценарий на выполнение интерпретатору pythonw, а не python, можно избежать появления консольного окна. Но в данном случае оно нам нужно для получения дополнительной информации.

Рисунок 3

Рисунок 3

Рисунок 4

Рисунок 4

Таким образом, потратив немного усилий, нам удалось существенно упростить для пользователя работу с FTP. Описав в файле config.py все операции, которые должны будут выполняться пользователями, мы получим удобное графическое окно. Каждая операция будет запускаться по одному щелчку мышки. В случае необходимости можно внести коррективы в список операций, на это уйдут считанные минуты.

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


Комментарии отсутствуют

Добавить комментарий

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

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru