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

Разделы

пятница, 20 мая 2011 г.

Классы хранилища и новая архитектура приложения

Наконец-то пришло озарение и я осилил доку по хранилищу. Вообщем этот пост формально ничего нового не будет содержать а здесь будет лишь только работа над ошибками. Первое что надо учесть, что в классе Model до чёртиков зарезервированных слов.
Так же зарезервировано подчёркивание где-только можно, надо быть с ним аккуратней. Итак как как в блоге не стоит цель сделать ревью сервисам гугл, а лишь практическое использование, то описание классов будет кратким:
Model - аналог таблицы
Expando - динамический класс, в котором хранятся объекты с различными свойствами.
PolyModel - это класс Model, с полиморфизмом.
ReferenceProperty - свойство которое используется как ссылка на другой объект и хранит его ключ. Непосредственно у ключей есть названия, я так понял это и есть способ обеспечить уникальность объектам. Как вы заметили на всё это ушёл без малого один день, но за этот день поменялась архитектура приложения, и появились так же некоторые идеи. Что мы имеем сегодня.
[trendec@localhost trendstat]$ ls -al
total 284
drwxrwxr-x 4 trendec trendec   4096 May 20 21:42 .
drwxr-xr-x 9 trendec trendec   4096 May 17 23:21 ..
-rw-r----- 1 trendec trendec    380 May 20 12:41 app.yaml
-rwxr-xr-x 1 trendec trendec  98154 May 14 00:26 bottle.py
-rw-r--r-- 1 trendec trendec 114796 May 18 19:47 bottle.pyc
-rw-r--r-- 1 trendec trendec     96 May 19 15:25 cron.yaml
-rwxrwxrwx 1 trendec trendec    471 May 20 21:30 index.yaml
-rwxr-x--- 1 trendec trendec    633 May 20 21:27 main.py
-rw-r--r-- 1 trendec trendec    515 May 20 19:38 models.py
-rw-r--r-- 1 trendec trendec   1051 May 20 19:38 models.pyc
-rw-r--r-- 1 trendec trendec   1796 May 20 19:49 news.py
-rw-r--r-- 1 trendec trendec   2244 May 20 19:49 news.pyc
-rw-r--r-- 1 trendec trendec    141 May 19 23:40 settings.py
-rw-r--r-- 1 trendec trendec    247 May 20 00:36 settings.pyc
drwxrwxr-x 2 trendec trendec   4096 May 18 20:36 static
-rw-r--r-- 1 trendec trendec    759 May 20 19:44 tasks.py
-rw-r--r-- 1 trendec trendec   1297 May 20 19:48 tasks.pyc
drwxrwxr-x 2 trendec trendec   4096 May 20 17:57 views
Уже явно прослеживается разбиение задач на подзадачи по списку файлов. А так же в детелях.


Листинг models.py:

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

from google.appengine.ext import db

class News(db.Model):
  title = db.StringProperty()
  url = db.LinkProperty()
  domain = db.LinkProperty()
  publisher = db.StringProperty()
  image = db.StringProperty()
  content = db.StringProperty()
  time = db.DateTimeProperty()

class Trends(db.Model):
  trend = db.StringProperty()
  time = db.DateTimeProperty(auto_now=True)
  video = db.StringListProperty()
  news = db.ListProperty(db.Key)
  ct = db.TextProperty()
  cf = db.TextProperty()
Таким образом все модели вынесли в отдельный файл


Листинг news.py:

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

from google.appengine.api import urlfetch
from urllib import quote
from settings import GOOLE_KEY
from datetime import datetime
from datetime import timedelta
import re
try:
  import json
except ImportError:
  import simplejson as json

def get_news(key):
  news = []
  rt = re.compile("<.*?>", re.I)
  rd = re.compile("^http://[^/]+", re.I)
  rn = re.compile("(.*?)($| [-+][012]\d\d\d$)", re.I)
  news_url = 'http://ajax.googleapis.com/ajax/services/search/news?v=1.0&q=%s&key=%s' % \
            (quote(key), GOOLE_KEY)
  res = urlfetch.fetch(news_url)
  if res.status_code == 200:
    jj = json.loads(res.content)
    if jj.has_key(u"responseStatus") and jj[u"responseStatus"] == 200:
      if jj.has_key(u"responseData") and jj[u"responseData"].has_key(u"results"):
        for item in jj[u"responseData"][u"results"]:
          d = {}
          d["title"] = rt.sub("", item[u"title"])
          d["url"] = item[u"unescapedUrl"]
          d["domain"] = rd.search(item[u"unescapedUrl"]).group(0) + "/"
          if item.has_key(u"content"):
            d["content"] = rt.sub("", item[u"content"])
            d["content"] = d["content"] if len(d["content"]) < 150 else "%s..." % d["content"][:147]
          else:
            d["content"] = ""
          d["image"] = item[u"image"][u"tbUrl"] if item.has_key(u"image") and item[u"image"].has_key(u"tbUrl") else ""
          d["publisher"] = item[u"publisher"] if item.has_key("publisher") else ""
          if item.has_key("publishedDate"):
            d["time"] = item[u"publishedDate"]
            rr = rn.match(d["time"])
            d["time"] = datetime.strptime(rr.group(1), "%a, %d %b %Y %H:%M:%S")
            tz = rr.group(2)
            if tz:
              d["time"] += timedelta(seconds = (-1 if tz[0] == '-' else 1)* int(tz[1:3])*60 + int(tz[3:]))
          news.append(d)
  return news
Новости потерпели некоторое изменения, в них появилась дополнительная информация, включая дату публикации (пока даже не знаю зачем).

Листинг tasks.py:

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

from google.appengine.ext import db
from google.appengine.api import urlfetch
from models import News
from models import Trends
from news import get_news
import re

def trends_update():
  trends = []
  trend_url = "http://www.google.com/trends/hottrends/atom/hourly"
  rl = re.compile("
  • .*?
  • ", re.I|re.S) rt = re.compile("<.*?>", re.I) res = urlfetch.fetch(trend_url) if res.status_code != 200: abort(404) content = res.content trends = rl.findall(content) trends = [rt.sub("", t).strip() for t in trends] for t in trends: item = Trends() item.trend = t for n in get_news(t): item.news.append(News.get_or_insert(n["url"], **n).key()) item.video = [] item.ct = "" item.cf = "" item.put()
    Этот скрипт вызывается кроном. И основной файл совсем похудел. Кстати, вынужден вас огорчить, или может обрадовать. Целиком файлы всего приложения публикуются в последний раз, это что бы сформировалось понимания всего приложения. В дальнейшем, что б не громоздить куски кода, это будут только новые методы или классы и может быть редко полностью файлы.

    Листинг main.py:

    #!/usr/bin/env python
    # encoding: utf-8
    
    from bottle import route, run, view, request, redirect, response, post, abort, debug, error
    from google.appengine.ext import db
    import tasks
    from models import News
    from models import Trends
    
    debug(True)
    
    
    @route('/')
    @route('')
    @view('home')
    def home_view():
      trends = []
      for t in Trends.all().order("-time").fetch(20):
        t.news_direct = News.get(t.news)
        trends.append(t)
      return {"trends" : trends }
    
    @route('/tasks/trends_update')
    @route('/tasks/trends_update/')
    def trends_update():
      tasks.trends_update()
    
    def main():
      run(server='gae')
    
    if __name__ == '__main__':
      main()
    Вот, это уже что-то. Напомню, это работающее приложение которое можно увидеть по адресу http://trendstat.appspot.com/.

    Комментариев нет:

    Отправить комментарий

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

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