вторник, 28 августа 2012 г.

Синхронизация git-репозитория на usb flash

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

Самое простое решение это архивация файлов проекта и копирование архива на флешку, но делать это каждый раз несколько утомительно. Гораздо удобнее воспользоваться возможностями, которые предоставляет git.

Создание локального репозитория в директории проекта:
git init 

Добавление файлов проекта в репозиторий:
git add . 

Допустим флешка смонтирована в директорию /media/usb, тогда следующая команда создаст копию репозитория на флешке:
git clone --local --bare --no-hardlinks . /media/usb/project/path

- обратите внимание на опцию --no-hardlinks, без этой опции команда выдает ошибку Invalid cross-deive link.

Чтобы не указывать каждый раз путь к копии репозитория добавим его с помощью команды:
git remote add usb file:///media/usb/project/path

Теперь после внесения изменений и коммита в локальный репозиторий можно переносить изменения на флешку командой:
git push usb

Для получения обновлений с флешки используется команда:
git pull usb master

Вместо master можно указать другую ветку.
При создании проекта на другой рабочей машине нужно скопировать репозиторий с флешки:
git clone file:///media/usb/project/path

Т.о. получаем удобную систему для синхронизации проекта между двумя и более рабочими машинами с помощью usb флешки или любого другого портативного носителя без использования центрального репозитория.

среда, 27 июня 2012 г.

Python-скрипт для получения курсов валют

Довольно часто встречается задача получения курсов валют и сохранение результатов в базу. Задача достаточно проста, чтобы привлекать для этого "тяжелую артиллерию" типа java, поэтому предлагается воспользоваться Python.
Почему Python?
Во-первых, это современный и популярный язык, во-вторых, python повсеместно используется для автоматизации задач в Linux, так что практически на любом хосте с Linux будет Python, ну и в-третьих интересно было попробовать Python на реальной задаче.
Итак, имеем базу данных на MSSQL 2000, в которую нужно сохранить список курсов валют на каждый день. В базу нужно сохранять не все доступные курсы, а только те, которые актуальны для приложения. Таблица со списком актуальных валют также находится в базе данных.
Еще имеется внешний ресурс, который выдает список курсов, он расположен по адресу http://www.cbr.ru/scripts/XML_daily.asp. Курсы выдаются в формате XML.
Алгоритм работы скрипта выглядит следующим образом:
  1.  Получить из базы список актуальных валют,
  2.  Получить и распарсить XML с курсами валют от cbr.ru,
  3.  Найти среди списка курсов курсы нужных валют и сохранить в базу,
  4.  Отправить сообщение на электронную почту с результатами работы скрипта.
Для работы скрипта понадобится следующее:
  1. Linux - ну это понятно, я использовал Ubuntu 12, но скорее всего подойдет любой современный дистрибутив,
  2. Python 2.6 и выше
  3. База данных, у меня используется MSSQL 2000
  4. Библиотека pymssql для создания запросов к БД
Про установку pymssql нужно рассказать чуть подробнее. Хотя для Ubuntu существует пакет deb, но он у меня не заработал, т.е. правильный запрос к базе не возвращал никаких результатов, при этом не выдавая какую-либо ошибку. Поэтому, по многочисленным советам я установил последнюю версию pymssql через pip.
Сначала установим pip:

sudo apt-get install python-pip 
Затем установим библиотеку Cython, которая нужна для сборки pymssql и не только его:
sudo pip install cython
И еще pyrex, если она не установлена
sudo pip install pyrex
После чего наконец можно установить pymssql:
sudo pip install pymssql

Примеры использования pymssql можно найти на странице проекта, а у меня получение списка валют выглядит следующим образом:
def load_currencies():
        currencies = []
        conn = pymssql.connect(host = 'dbhost', user='user',password='secret',database='my_database')
        try:
                cur = conn.cursor()
                cur.execute('SELECT ShortName, ID FROM [Currency] where not exists(select * from Currency_Exchange_Rate \
                        where ListID = %d and FromCurrency = Currency.ID and [Date] = dbo.GetDay(GETDATE()))', 1)
                row = cur.fetchone()
                while row:
                        currencies.append({'Name':row[0], 'ID':row[1]})
                        row = cur.fetchone()
        finally:
                conn.close()
        return currencies
Функция load_currencies возвращает список актуальных валют, для которых не установлен курс на текущую дату.

Затем, получение и парсинг списка курсов валют:
u = urllib2.urlopen("http://www.cbr.ru/scripts/XML_daily.asp", timeout=10)
parser = xml.sax.make_parser()
handler = CurrencyRateHandler()
parser.setContentHandler(handler)
parser.parse(u)
print handler.mapping

Для загрузки файла используется метод urlopen из библиотеки urllib2. Он возвращает поток, который можно сохранить в файл, а можно напрямую передать парсеру, как в данном случае. Для разбора полученного XML используется стандартный SAX-парсер. Чтобы парсер мог что-то сделать с полученным файлом ему необходим обработчик. Код обработчика представлен в классе CurrencyRateHandler, который наследуется от библиотечного класса xml.sax.handler.ContentHandler. Код обработчика:
class CurrencyRateHandler(xml.sax.handler.ContentHandler):
        def __init__(self):
                self.inTag = ""
                self.charCode = ""
                self.mapping = {}
        def startElement(self, name, attributes):
                if name in set(["CharCode","Nominal", "Value"]):
                        self.buffer = ""
                        self.inTag = name
                else:
                        self.inTag = ""
        def characters(self, data):
                if self.inTag:
                        self.buffer += data
        def endElement(self,name):
                if name == "CharCode":
                        self.charCode = self.buffer
                        self.mapping[self.charCode] = {}
                elif name == "Nominal":
                        self.mapping[self.charCode][self.inTag] = int(self.buffer)
                elif name == "Value":
                        self.mapping[self.charCode][self.inTag] = float(self.buffer.replace(',','.'))
                self.inTag = ""
SAX-обработчик получает события startElement и endElement при обработке открывающего и закрывающего тегов XML соответственно. Содержимое тега поступает в метод characters. Обработчик устроен достаточно просто он проверяет на соответствие имени тега с определенными шаблонами и аггрегирует данные в словарь mapping. В результате разбора получается словарь ключами, которого являются коды валют, а значениями словари, каждый из которых содержит информацию о курсе валюты и номинале обмена (1, 10 или 100 единиц). Затем нам нужно из полученного от парсера словаря отфильтровать только те валюты, которые нас интересуют и записать значения их курсов в базу. Фильтрация словаря выполняется с помощью функции filter:
update_rates(filter(lambda c: rates.has_key(c['Name']), currencies), rates)
Функция update_rates выполняет запись в базу следующим образом:
def update_rates(currencies, rates):
        conn = get_connection()
        try:
                cur = conn.cursor()
                cur.executemany('INSERT INTO Currency_Exchange_Rate(Date,ListID, FromCurrency, Factor,Amount)\
        VALUES(dbo.GetDay(GETDATE()),1,%d,%d,%s)', map(lambda c: (c['ID'], rates[c['Name']]['Nominal'], rates[c['Name']]['Value']), currencies))
                conn.commit()
        finally:
                conn.close()
самое интересное в этой функции находится в строке 6, там для формирования списка значений для команды INSERT используется функция map и лямбда-функция, возвращающая кортеж значений для вставки в базу из словаря курсов валют. Вот в общем-то и все, остаются всякие полезные мелочи вроде логгирования и отправки сообщения по электронной почте. Полученный скрипт можно поместить в /etc/cron.daily, чтобы он выполнялся ежедневно по расписанию. Для этого в заголовке нужно указать оболочке какой интерпретатор следует использовать (python конечно же):
#!/usr/bin/env python
import sys
import pymssql
import urllib2
import xml.sax.handler
и дать файлу разрешение на выполнение:
sudo chmod 744 updater.py

понедельник, 4 июня 2012 г.

Ubuntu и FireFox 12 по-русски

После очередного обновления у FireFox на 12ю версию интерфейс браузера неожиданно стал англоговорящим.
Система Linux Mint 12.
Пакет firefox-locale-ru установлен.
Удаление пакета firefox-locale-en не помогает.
По совету из форума  на сайте mozilla-russia нашел свою версию FireFox и установил LangPack (который в колонке Русификация).
Задача решена - интерфейс русский.

четверг, 8 октября 2009 г.

POJO без лишних слов

Нашел тут очень интересную штуку, называется Lombok.
Особенно интересно - интеграция с Eclipse.
Если коротко, то это плагин к компилятору, который на этапе генерации кода вставляет геттеры/сеттеры для указанных полей, что позволяет не держать их в исходном коде. Не секрет, что в 90% случаев геттеры и сеттеры используются только для доступа к полям (fields) и не содержат бизнес-логики.
В качестве примера, для pure java надо писать так:

class Book{
private String title;
private String author;
private int pageNumber;
public String getTitle(){
return title;
}
public String getAuthor(){
return author;
}
public int getPageNumber(){
return pageNumber;
}
public void setTitle(String title){
this.title = title;
}
public void setAuthor(String author){
this.author = author;
}
public void setPageNumber(int pageNumber){
this.pageNumber = pageNumber;
}
}
а с lombok достаточно написать:
class Book{
@Getter @Setter private String title;
@Getter @Setter private String author;
@Getter @Setter private int pageNumber;
}
По компактности и выразительности кода - почти Groovy :)

Информация по использованию - после скачивания надо будет запустить полученный jar и интегрировать его в Eclipse (там графический инсталлятор, так что все интуитивно понятно). Интегрировать с Eclipse не обязательно, но добавит немного приятности.
Необходимо также чтобы при компиляции lombok.jar был в classpath проекта (для Eclipse его надо добавить к библиотекам проекта). В режиме исполнения (runtime) ломбок не нужен.

И немного дегтя: встроить ломбок в большой проект управляемый maven у меня не получилось, выдается куча ошибок типа InvalidClassModification. Разбираться было некогда да и ни к чему, наверное. Возможно, в следующих версиях добавят поддержку maven. А для проектов собираемых более просто - возможно самое то, что нужно.

Да и кстати, возможности не ограничиваются геттерами/сеттерами - есть еще несколько интересных возможностей. Более подробно рассмотренные в примерах на сайте.

четверг, 13 августа 2009 г.

Методика разработки

Методика разработки это по сути то, с чего начинается любой проект. В данном случае, я не имею в виду процессы, типа RUP или XP или еще какой P, а скорее конфигурацию проекта.
Цель этого текста не дать какой-то рецепт, а скорее структурировать собственные знания.
Да, и следует иметь в виду что все это написано в контексте разработки web-приложений на java.
Каждый раз начиная новый проект приходится отвечать на вопросы:
  1. Будет ли это одномодульный или многомодульный проект?
  2. Каким образом будет осуществляться взаимодействие между участниками?
  3. Какова область ответственности каждого участника?
  4. Какие средства стоит использовать для сборки, тестирования и прочего?
И при этом всякий раз происходят внутренние колебания, сомнения, возникают дискуссии внутри команды и т.д. Вообще-то это нормальный процесс, но со временем возникает ощущение что на самом деле существует довольно-таки ограниченный выбор решений (или шаблонов, если угодно) из которого можно выбирать подходящее для конкретного проекта и дополнять/упрощать его, если уж проект такой уникальный.
Вот собственно я и хочу попробовать набросать несколько таких шаблонов, чтобы облегчить себе и коллегам такой выбор.

"Мелкое приложение"
Как следует из названия шаблон предназначен для небольших приложений, как правило, разрабатываемых одним автором. Как определить масштаб приложения каждый выбирает индивидуально, конечно. Я бы определил по числу используемых сущностей, в данном случае, не более 5-6. Что характерно, при таком определении количество пользователей (а значит и нагрузка) и/или размер базы могут быть весьма и весьма не маленькими.
1. Модульность - проект одномодульный во избежание излишнего усложнения, тем более для одного разработчика. Понятие модуля, обычно, соответствует понятию проект в средах разработки. Во всяком случае, для Eclipse это тождественные понятия. Но даже в случае коллективной работы сложностей не должно быть, если:
2. Средства коллективной разработки - subversion. Конечно и вопрос звучит несколько шире и существуют и другие системы контроля версий, помимо subversion, но в данном контексте, думаю, этого достаточно.
3. Область ответственности - т.к. разработчик скорее всего один, то он несет ответственность за приложение. Если для каких-то целей привлекаются дополнительные участники, например, web-дизайнер, то в этом случае четко должна быть определена их зона ответственности. Для дизайнера соответственно это будет внешнее оформление приложения: шаблоны, графические элементы и пр.
4. Вспомогательные средства
4.1 Сборка - в данном случае уместно использовать Аnt, как относительно простое, надежное и независимое от IDE средство.
4.2 Тестирование - JUnit, а также его различные расширения в зависимости от особенностей проекта и требованиям к качеству.
В общем-то и все. Можно использовать еще много разных средств для улучшения качества кода, поиска ошибок, но, как мне кажется достаточный уровень для разработки достигнут.

Ждем вдохновения и новые шаблоны проектов.