Friday, June 20, 2008

Использование Google Search API и Google Translate API в Python

Как известно, Google предоставляет множество различных API для доступа к своим сервисам. Список и описание их можно найти здесь. Я же хочу остановиться на двух весьма удобных сервисах от Google - поиске и переводе. Точнее, хочется предложить простенький рецепт по использованию предоставляемых для них API из Python.

Google Search API и Google Translate API представляют собой AJAX API: вызовы функций - это HTTP запросы, ответ возвращается в формате JSON. В связи с последним нам понадобится библиотека simplejson для парсинга ответов. Итак, начнем с перевода.

def translate(text,langpair):
import urllib
import simplejson

query = urllib.urlencode({'q' : text.encode("utf-8"),'langpair':langpair})
url = u'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&%s'.encode("utf-8") \
% (query)
search_results = urllib.urlopen(url)
json = simplejson.loads(search_results.read())
mess = json['responseData']['translatedText']
return mess

translated = translate("I am feeling lucky","en|ru")
print "Translated:", translated
Не сложнее и API для поиска:
def search(text):
import urllib
import simplejson

query = urllib.urlencode({'q' : text.encode("utf-8")})
url = u'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&%s'.encode("utf-8") \
% (query)
search_results = urllib.urlopen(url)
json = simplejson.loads(search_results.read())
results = json['responseData']['results']
return results

results = search("text to search")
title = results[0]['title']
noh_title = title.replace('', '').replace('', '')
url = results[0]['url']
print noh_title+": "+url # Мне повезет! :)
Для работы API не требуется регистрации, что весьма приятно. Удачного применения!

Monday, June 9, 2008

Апплет для Gnome на Python

Постановка задачи.

Как и многим другим разработчикам, мне приходится контролировать затраченное на работу время - для написания тайм-репортов и даже просто самоконтроля. Конечно, можно было бы использовать готовые приложения, например Gnome Time Tracker, но у всех подобных программ есть один существенный для меня недостаток - они требуют к себе слишком много внимания. К тому же, на моих предпочтениях сказывается и специфика работы - я работаю в офисе на постоянной основе, а отчеты составляю лишь к концу месяца, при этом особая детализация в этих отчетах не требуется - нужно лишь предоставить временные интервалы. Т.о., мне нужно небольшое приложение, при помощи которого я смог бы легко записывать время прихода и ухода. Задача действительно тривиальна, написание подобной утилитки под силу любому школьнику.
Однако, чтобы было интересней, я решил оформить решение в виде апплета для панели Gnome. Так приложение будет всегда на виду и легко доступно, а при желании я смогу и расширить его функциональность. Задача немного усложняется тем, что из тех мануалов о написании апплетов, что я нашел, все были устаревшими, так что многое пришлось подбирать методом проб и ошибок.
Но не будем слишком углубляться. Для начала, нам понадобится подобный .server файл, описывающий фабрику(TimeTrackApplet_Factory.server):
<oaf_info>
<oaf_server iid="OAFIID:TimeTrackApplet_Factory" type="exe"
location="/usr/lib/gnome-panel/timetrackapplet">

<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:Bonobo/GenericFactory:1.0"/>
<item value="IDL:Bonobo/Unknown:1.0"/>
</oaf_attribute>
<oaf_attribute name="name" type="string" value="Time Track Applet Factory"/>
<oaf_attribute name="description" type="string" value="Factory to create the time track applet"/>
</oaf_server>

<oaf_server iid="OAFIID:TimeTrackApplet" type="factory"
location="OAFIID:TimeTrackApplet_Factory">

<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
<item value="IDL:Bonobo/Control:1.0"/>
<item value="IDL:Bonobo/Unknown:1.0"/>
</oaf_attribute>
<oaf_attribute name="name" type="string" value="Time Track Applet"/>
<oaf_attribute name="description" type="string" value="TimeTrack applet"/>
<oaf_attribute name="panel:category" type="string" value="Amusements"/>
<oaf_attribute name="panel:icon" type="string" value="myicon.png"/>
</oaf_server>
</oaf_info>
Содержимое файла говорит само за себя. Если кратко - здесь содержится описание нашего апплета и всего, что нужно для его запуска. Стоит отметить, что название фабрики - TimeTrackApplet_Factory должно совпадать с именем файла. Также стоит запомнить путь /usr/lib/gnome-panel/timetrackapplet - это место, где будет размещаться исполняемый файл апплета. Естественно, при желании можно менять любые описания, названия и иконки.
Обычно этот .server файл помещается в каталог /usr/lib/bonobo/servers/. После того, как мы создали и поместили этот файл в нужное место, перейдем к самому апплету. Ниже я приведу полный исходный код, благо он не очень велик:
#! /usr/bin/env python
# -*- coding: UTF-8 -*-

import pygtk
pygtk.require('2.0')
import gtk
import gobject
import gnome
import gnomeapplet

import logging
from datetime import datetime

logging.basicConfig(filename="path/to/applet.log") # я решил, что простого сохранения в лог мне хватит :)
log = logging.getLogger("TimeTrack")
log.setLevel(logging.INFO)

class TimeTrackApplet(gnomeapplet.Applet):# основной класс
def update_tooltip(self, text = None):
if text is None:
self.tooltip.set_tip(self.applet, self.tooltip_text)
else:
self.tooltip_text = text
self.update_tooltip()

def update_text(self,text=None):
if text is None:
self.temp.set_text(self.data)
else:
self.data = text
self.update_text()

def create_applet(self): # создание апплета, самая смыслонагруженная часть
app_window = self.applet
event_box = gtk.EventBox()
event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.POINTER_MOTION_HINT_MASK |
gtk.gdk.CONFIGURE )

self.temp = gtk.Label()
self.data = "TT" # я не стал искать иконку и ограничился надписью TT
self.update_text()

self.inside_applet = gtk.HBox()
self.inside_applet.pack_start(self.temp)

self.tooltip = gtk.Tooltips() # в качестве демонстрации, создадим также всплывающую подсказку
self.tooltip_text = "Middle click for tooltip update"
self.update_tooltip()

event_box.add(self.inside_applet)
app_window.add(event_box)
app_window.show_all()
return event_box

def button_pressed(self, widget, event, *args, **kwargs):
if event.button==2:# средняя кнопка мыши, обновить подсказку
self.update_tooltip(datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"))
elif event.button==1:# левая кнопка мыши, записать в лог
log.info("Button Pressed: %s",datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S"))

def __init__(self,applet,iid):
self.__gobject_init__()
self.applet = applet

self.box = self.create_applet()
self.applet.connect("button-press-event",self.button_pressed) # добавляем обработчик события клика
log.info("Applet created succesfully")

gobject.type_register(TimeTrackApplet)

DEBUG = False

def timetrack_applet_factory(applet, iid): # Реализация фабрики
log.info("Creating the applet...")
TimeTrackApplet(applet, iid)
return True

log.info("Starting...")
gnomeapplet.bonobo_factory("OAFIID:TimeTrackApplet_Factory",
TimeTrackApplet.__gtype__,
"TimeTrack",
"0.1",
timetrack_applet_factory) # Запуск всего механизма
log.info("Stopping...")
Данный файл нужно скопировать на то место, которое указано в .server файле. Большая часть кода - gnome-specific, поэтому я постарался объяснить только самые общие моменты(экскурс в gnome+python не является моей целью сейчас) при помощи комментариев.
Итак, для тех, кто код прочитал( :) ), понятно, что по щелчку левой кнопкой мыши дата и время щелчка (в UTC) записываются в лог-файл. Для меня это удобно - в конце месяца я смогу восстановить по этому файлу временные интервалы, а если когда забуду щелкнуть, то вряд ли будет трудно разобраться в этом человеку(это объяснение тому, что я не добавил в утилиту возможность автоматического подсчета интервалов).
Надеюсь, этот простой пример поможет кому-нибудь в написании собственных апплетов. Удачи!

Ссылки по теме:

-http://www.gnome.org/projects/ORBit2/appletstutorial.html (примеры на С)
-http://www.pygtk.org/articles/applets_arturogf/ (устаревший пример на Python)
-http://computertemp.berlios.de/ (готовый апплет, наиболее полезным оказался)
-http://www.pygtk.org/docs/pygtk/ (reference для pygtk)

Monday, June 2, 2008

Небольшое дополнение к отладчику pdb

В последнее время почему-то возникают значительные трудности с написанием каких-либо постов, несмотря на то, что тем, которые можно было бы осветить - множество. Так, я забыл написать об Exception #08, от которого у меня остались неизгладимые впечатления(благо, отчеты с Exception уже есть и я не уверен, что у меня получилось бы лучше). Также весьма жалею о том, что не могу осветить некоторые глубоко интересные мне темы, по большей части связанные с программированием на Python.
Дабы хоть как-то обновить наполнение, решил предложить нижеприведенный простенький рецепт.

Итак, несмотря на то, что существует множество весьма удобных IDE, которые предоставляют графические инструменты для отладки Python приложений, иногда бывает намного удобнее воспользоваться отладчиком pdb - он позволяет вам очень быстро окунуться именно в ту часть кода, которая вам нужна. Как известно, самый простой способ использования - это прописать

import pdb
pdb.set_trace()
в то место, где вы хотите прервать выполнение, а после этого командами step(s) и next(n) продвигаться по коду. Также стоит отметить, что неопознанные команды распознаются как Python-команды, что позволяет выполнять какой-либо проверочный код прямо в ходе отладки. Для того, чтобы не заблудиться в отлаживаемом и проверочном коде часто используется команда list (l), которая показывает исходный код места, в котором сейчас остановилось выполнение. Одним из недостатков данной команды можно считать то, что последовательное выполнение нескольких команд list действительно "листает" код, т.е., если два раза выполнить команду list, то будет сначала показано несколько строчек, обрамляющих текущую строку, а потом - следующие несколько строчек. Такое поведение может очень раздражать, если вы хотите просто вспомнить, а где же вы сейчас находитесь, вместо того, чтобы листать код.
Т.о., появляется желание немного дополнить pdb, чтобы добавить возможность просматривать код без пролистыванья. Благодаря динамичности Python, можно предложить такое решение:
def do_ll(self,arg):
return self.do_list(str(self.curframe.f_lineno))

import pdb

pdb.Pdb.do_ll = do_ll
pdb.set_trace()
Этот код добавляет новую команду ll (LL маленькими буквами), которая пользуется возможностями команды list выводить определенные строки кода. При этом очень важно то, что исходный код pdb не меняется.

Проверено на Python 2.4.4