Делаем mash-up приложение на трэндах

Разделы

вторник, 17 апреля 2012 г.

С возвращением.

Вообщем прошёл почти год, и я был занят в другой области - не связанной с программированием, и вот решил вернутся. За это время квоты... тут вообще жёсткий сюрприз, урезали по самые яйца, так что дальше не куда. Приложение тоже придётся немного переориентировать - сделать ещё проще. Да! А почему бы нет, лучше доделать самое простое и посмотреть как оно будет. Пришлось переименовать из 'uptweetter' в 'trendstat' (в блоге уже всё исправил задним числом). Вообщем, "убрал" весь интерактив, оставил только выдачу из БД в разном формате, и немного остаётся задумать над дизайном. А весь интерактив будет в следующем проекте, очень скоро (правда скоро) на русском языке, но и вообще под рунет и с твиттером. Идею пока палить не буду.

воскресенье, 29 мая 2011 г.

OAuth для твиттера (продолжение)

Буду краток. Вчера времени не хватило, не успел выход сделать:
@route('/exit')
@route('/exit/')
def exit_view():
    del_cookie()
    redirect("/")
И кстати заодно файлик для работы с куками:
Листинг cookie.py:
@route('/login')
#!/usr/bin/env python
# encoding: utf-8

from bottle import route, run, view, request, redirect, response, post, abort, debug, error
import logging
import hmac
import uuid
import base64
from google.appengine.ext import db
from datetime import datetime
from datetime import timedelta

from models import Users
from oauth import TwitterClient


def generate_rnd_hash(uid):
    h = hmac.new(str(uid))
    h.update(uuid.uuid4().bytes)
    return base64.b64encode(h.digest() + uuid.uuid4().bytes, ['+', '-']).replace('=', '').replace('+', '')

    
def set_cookie(uid, user_info = {}):
    last_error = "ok"
    hash = generate_rnd_hash(uid)
    dt = datetime.utcnow() + timedelta(seconds = 60*60*24*7)
    exp_gmt = dt.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
    user = Users.get_by_key_name(str(uid))
    if not user:
        user = Users(key_name=str(uid))
    if user_info:
        user.uid = user_info["uid"]
        user.username = user_info["username"]
        user.name = user_info["name"]
        user.image = user_info["image"]
        user.token = user_info["token"]
        user.secret = user_info["secret"]
    user.hash = hash
    user.hashex = dt
    user.put()
    try:
        response.set_cookie('sid', hash, secret = None, \
                                            **{'expires' : exp_gmt, 'path' : '/'})
    except Exception, msg:
        logging.error(msg)

def del_cookie():
    uid = get_cookie()
    user = Users.get_by_key_name(str(uid)) if uid else None
    if user:
        user.delete()
    try:
        response.set_cookie('sid', "", secret = None, \
                                            **{'path' : '/'})
    except Exception, msg:
        logging.error(msg)
        
    
def get_cookie():
    hash = request.get_cookie("sid")
    logging.debug(hash)
    key = Users.all(keys_only=True).filter("hash = ", hash).fetch(1)
    key = long(key[0].name()) if key else 0
    return key

OAuth для твиттера

Этот интерфейс авторизации придумали для того что бы свой пароль пользователь не оставлял на третьих сайтах, а авторизовался непосредственно там он создал аккаунт - у провайдера. В данном примере речь пойдёт о Twitter.
Итак мы зарегистрировали приложение в предыдущем посте. И получили два пин-кода: ключ, и секрет. С помощью их мы можем теперь получить, если пользователь разрешит, пользовательский токен, и секрет - пользовательские пин-коды. Ключей везде по двое, что бы обеспечьте защиту канала связи.
По большому счёту если нет желания со всем этим заморачиваться - то и не надо, для это есть уже готовые библиотеки, сделанные прямо для авторизации в твиттере через GAE-приложение, и даже не одна. Я взял AppEngine-OAuth-Library by Mike Knapp, которая состоит из одного файла и маленьким примерчиком.
Собственно весь процесс авторизации выглядит следующим образом:
@route('/login')
@route('/login/')
def login_view():
    callback_url = "http://%s/verify" % request.environ["HTTP_HOST"]
    client = TwitterClient(TWITTER_KEY, TWITTER_SECRET, callback_url)
    redirect(client.get_authorization_url())
    
@route('/verify')
@route('/verify/')
def verify_view():
    callback_url = "http://%s/verify" % request.environ["HTTP_HOST"]
    auth_token = request.GET.get('oauth_token', "")
    auth_verifier = request.GET.get('oauth_verifier', "")
    client = TwitterClient(TWITTER_KEY, TWITTER_SECRET, callback_url)
    try:
        user_info = client.get_user_info(auth_token, auth_verifier=auth_verifier)
    except Exception, msg:
        redirect("/")
    set_cookie(user_info["uid"], user_info)
    redirect("/")
Собственно запрос на авторизацию и её проверка. В случаее успеха имеем словарик с
service, token, secret, id, username, name, picture. Теперь в твиттер можно отправлять авторизованные запросы:
timeline_url = "http://twitter.com/statuses/user_timeline.xml"
    result = client.make_request(url=timeline_url, token=user_token, 
          secret=user_secret)

суббота, 28 мая 2011 г.

Твиттер

Как я уже сообщил что одну неделю приложение было не работоспособно из за бага. За это время пришло понимание каким наконец приложение должно быть. И будет оно таким:
  • твиттер-ориентированным, или дословно твиттер-приложением.
  • больше ориентированно на сами твиты, чем на фолловеров.
  • ну и новости, тредны тоже должны быть.
Как это удастся связать - это для меня тоже вопрос, но пока осваиваем твиттер. Причём с самого начала (*sick*). Для этого надо завести аккаунт. После подтверждения по емэйлу можно зарегистрировать свое приложение. Приблизительно форма регистрации приложения выглядит так:
  • Application Name - уникальное название приложения
  • Description - описание.
  • Application Website - домашняя страница приложения.
  • Organization - можно указать себя.
  • Application Type - либо вэб, либо десктопное.
  • Callback URL - если десктопное, то надо указать куда будет перенаправлять твиттер авторизованных, через OAuth пользователей (подробней об этом уже в следующем посте).
  • Application Icon - иконка.

После ввода капчи, приложение можно считать зарегистрированным.

Индексы

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

суббота, 21 мая 2011 г.

Певая страница

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

Но что б статья не была совсем уж голой, я скажу два слова о шаблонах. После того как вчера была проделана огромная работа по адаптации архитектуры к App Engine. Под вечер провозился с глупой ошибкой в шаблоне, в связи с этим перелопатил все доступные шаблоны. Я пользуюсь стандартным из Bottle и на нём пока останусь. Интересным из всех показался только Mako. Не смотря на обилие файлов в библиотеке по скорости он не уступает стандартному (но всё равно это гораздо медленней чем чистый питон), а возможности у него сопоставимы с самим языком. т.е. он фактически обеспечивает прозрачность HTML-кода для питона. И прям в HTML объявлять функции, обрабатывать переменные. Вообщем, при разработке большого проекта вещь вполне достойная.

Видео

Что нужно для mash-up приложения? Конечно видео. Ведь как же без него, с релевантным подбором могут быть проблемы, но это уже время покажет. Вообщем примерчик как это сделать располагается здесь. Одно не понятно зачем ключ? Немного полазив понял что он используется при защищенном соединении и с пользовательским данными. Нам это пока не надо, но ключик я получил.

Чуть подробней о том же уже сюда. Что ж делаем HTTP-запрос, на получить одно какое-нибудь видео, получаем JSON размером в "Войну и мир", если учесть то что от видео мы хотели получить только его идентификатор. По поводу хедера ещё были сомнения: слать его или нет. Решил так раз ключ получил буду слать. Получилось вот что.

Листинг videos.py

#!/usr/bin/env python
# encoding: utf-8

from google.appengine.api import urlfetch
from urllib import quote
from settings import YOUTUBE_KEY
try:
  import json
except ImportError:
  import simplejson as json

def get_videos(key):
  videos = []
  video_url = 'http://gdata.youtube.com/feeds/api/videos?v=2&alt=json&q=%s&orderby=relevance&max-results=10&format=5&time=today' % \
            quote(key)
  headers = {"GData-Version": '2', "X-GData-Key" : "key=%s" % YOUTUBE_KEY}
  res = urlfetch.fetch(video_url, headers = headers)
  if res.status_code == 200:
    jj = json.loads(res.content)
    if jj.has_key(u"feed") and jj[u"feed"].has_key(u"entry"):
      for item in jj["feed"]["entry"]:
        videos.append(item[u"media$group"][u"yt$videoid"][u"$t"])
  return videos

Постоянные читатели

Copyright © trendec. Технологии Blogger.