From f01e43401b69a1540560dab2c359ad7574a8b8e4 Mon Sep 17 00:00:00 2001 From: "luxagraf@c63593aa-01b0-44d9-8516-4b9c7e931d7f" Date: Sun, 11 Jan 2009 03:47:34 +0000 Subject: added tumblr client code --- apps/blog/models.py | 10 ++ apps/links/models.py | 8 +- apps/links/tumblr.py | 239 +++++++++++++++++++++++++++++++++ apps/links/utils.py | 36 +++-- base_urls.py | 8 +- templates/feeds/blog_description.html | 1 + templates/feeds/links_description.html | 1 + 7 files changed, 290 insertions(+), 13 deletions(-) create mode 100644 apps/links/tumblr.py create mode 100644 templates/feeds/blog_description.html create mode 100644 templates/feeds/links_description.html diff --git a/apps/blog/models.py b/apps/blog/models.py index d9cea69..e598f14 100644 --- a/apps/blog/models.py +++ b/apps/blog/models.py @@ -98,4 +98,14 @@ class BlogSitemap(Sitemap): def lastmod(self, obj): return obj.pub_date +class LatestFull(Feed): + title = "Luxagraf: Topographical Writings" + link = "/writing/" + description = "Latest postings to luxagraf.net" + description_template = 'feeds/blog_description.html' + + def items(self): + return Entry.objects.filter(status__exact=1).order_by('-pub_date')[:10] + + #signals.post_save.connect(create_location_item, sender=Entry) \ No newline at end of file diff --git a/apps/links/models.py b/apps/links/models.py index cac4aa2..00a6f54 100644 --- a/apps/links/models.py +++ b/apps/links/models.py @@ -41,7 +41,7 @@ class Link(models.Model): return self.title def get_absolute_url(self): - return "/link/%s/" % (self.id) + return self.url def get_model_name(self): return 'link' @@ -73,9 +73,9 @@ class LinkSitemap(Sitemap): class LatestLinks(Feed): title = "Luxagraf: Links" - link = "/collections/" - description = "Latest Links posted to luxagraf.net" - description_template = 'feeds/collections_description.html' + link = "http://ma.gnolia.com/people/luxagraf/bookmarks" + description = "Links to interesting stuff" + description_template = 'feeds/links_description.html' def items(self): return Link.objects.filter(status__exact=1).order_by('-pub_date')[:10] diff --git a/apps/links/tumblr.py b/apps/links/tumblr.py new file mode 100644 index 0000000..09c3394 --- /dev/null +++ b/apps/links/tumblr.py @@ -0,0 +1,239 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright 2008 Ryan Cox ( ryan.a.cox@gmail.com ) All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +'''A wrapper library for Tumblr's public web API: http://www.tumblr.com/api''' + +__author__ = 'ryan.a.cox@gmail.com' +__version__ = '0.1' + + +from urllib2 import Request, urlopen, URLError, HTTPError +from urllib import urlencode, quote +import base64 +import re + +try: + import simplejson +except ImportError: + from django.utils import simplejson + +GENERATOR = 'python-tumblr' +PAGESIZE = 50 + + +class TumblrError(Exception): + ''' General Tumblr error ''' + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return self.msg + +class TumblrAuthError(TumblrError): + ''' Wraps a 403 result ''' + pass + +class TumblrRequestError(TumblrError): + ''' Wraps a 400 result ''' + pass + +class TumblrIterator: + def __init__(self, name, start, max, type): + self.name = name + self.start = start + self.max = max + self.type = type + self.results = None + self.index = 0 + + def __iter__(self): + return self + + def next(self): + if not self.results or (self.index == len(self.results['posts'])): + self.start += self.index + self.index = 0 + url = "http://%s.tumblr.com/api/read/json?start=%s&num=%s" % (self.name,self.start, PAGESIZE) + if self.type: + url += "&type=" + self.type + response = urlopen(url) + page = response.read() + m = re.match("^.*?({.*}).*$", page,re.DOTALL | re.MULTILINE | re.UNICODE) + self.results = simplejson.loads(m.group(1)) + + if (self.index >= self.max) or len(self.results['posts']) == 0: + raise StopIteration + + self.index += 1 + return self.results['posts'][self.index-1] + +class Api: + def __init__(self, name, email=None, password=None ): + self.name = name + self.is_authenticated = False + self.email = email + self.password = password + + def auth_check(self): + if self.is_authenticated: + return + url = 'http://www.tumblr.com/api/write' + values = { + 'action': 'authenticate', + 'generator' : GENERATOR, + 'email': self.email, + 'password' : self.password } + + data = urlencode(values) + req = Request(url, data) + try: + response = urlopen(req) + page = response.read() + self.url = page + self.is_authenticated = True + return + except HTTPError, e: + if 403 == e.code: + raise TumblrAuthError(str(e)) + if 400 == e.code: + raise TumblrRequestError(str(e)) + except Exception, e: + raise TumblrError(str(e)) + + + def write_regular(self, title=None, body=None, **args): + if title: + args['title'] = title + if body: + args['body'] = body + args = self._fixnames(args) + if not 'title' in args and not 'body' in args: + raise TumblrError("Must supply either body or title argument") + + self.auth_check() + args['type'] = 'regular' + return self._write(args) + + def write_photo(self, source=None, **args): + if source: + args['source'] = source + + args = self._fixnames(args) + if 'source' in args and 'data' in args: + raise TumblrError("Must NOT supply both source and data arguments") + + if not 'source' in args and not 'data' in args: + raise TumblrError("Must supply source or data argument") + + self.auth_check() + args['type'] = 'photo' + return self._write(args) + + def write_quote(self, quote=None, **args): + if quote: + args['quote'] = quote + args = self._fixnames(args) + if not 'quote' in args: + raise TumblrError("Must supply quote arguments") + + self.auth_check() + args['type'] = 'quote' + return self._write(args) + + def write_link(self, url=None, **args): + if url: + args['url'] = url + args = self._fixnames(args) + if not 'url' in args: + raise TumblrError("Must supply url argument") + + self.auth_check() + args['type'] = 'link' + return self._write(args) + + def write_conversation(self, conversation=None, **args): + if conversation: + args['conversation'] = conversation + args = self._fixnames(args) + if not 'conversation' in args: + raise TumblrError("Must supply conversation argument") + + self.auth_check() + args['type'] = 'conversation' + return self._write(args) + + def write_video(self, embed=None, **args): + if embed: + args['embed'] = embed + args = self._fixnames(args) + if 'embed' in args and 'data' in args: + raise TumblrError("Must NOT supply both embed and data arguments") + + if not 'embed' in args and not 'data' in args: + raise TumblrError("Must supply embed or data argument") + + self.auth_check() + args['type'] = 'video' + return self._write(args) + + def _fixnames(self, args): + for key in args: + if '_' in key: + value = args[key] + del args[key] + args[key.replace('_', '-')] = value + return args + + def _write(self, params, headers=None): + self.auth_check() + url = 'http://www.tumblr.com/api/write' + params['email'] = self.email + params['password'] = self.password + params['generator'] = GENERATOR + data = urlencode(params) + if headers: + req = Request(url, data, headers) + else: + req = Request(url, data) + newid = None + try: + urlopen(req) + raise TumblrError("Error writing post") + + except HTTPError, e: + if 201 == e.code: + newid = e.read() + return self.read(id=newid) + raise TumblrError(e.read()) + + def read(self, id=None, start=0,max=2**31-1,type=None): + if id: + url = "http://%s.tumblr.com/api/read/json?id=%s" % (self.name,id) + response = urlopen(url) + page = response.read() + m = re.match("^.*?({.*}).*$", page,re.DOTALL | re.MULTILINE | re.UNICODE) + results = simplejson.loads(m.group(1)) + if len(results['posts']) == 0: + return None + + return results['posts'][0] + else: + return TumblrIterator(self.name,start,max,type) + +if __name__ == "__main__": + pass diff --git a/apps/links/utils.py b/apps/links/utils.py index a575f4f..2649dde 100644 --- a/apps/links/utils.py +++ b/apps/links/utils.py @@ -1,4 +1,4 @@ -import time, datetime +import time, datetime, urllib from django.core.exceptions import ObjectDoesNotExist from django.template.defaultfilters import slugify,striptags @@ -7,7 +7,6 @@ from django.utils.encoding import smart_unicode from strutils import safestr,unquotehtml from APIClients import MagnoliaClient -from urlgrabber.grabber import URLGrabber import pydelicious as delicious import markdown2 as markdown @@ -64,6 +63,9 @@ def sync_magnolia_links(*args, **kwargs): email_link(l) send_to_delicious(l) + if l.status == 1: + post_to_tumblr(l) + send_to_deliciousfb(l) if(dupe): break """ @@ -123,11 +125,29 @@ def send_to_delicious(link): tags = link.tags.split(',') for tag in tags: del_tags += tag.strip().replace(' ','_')+' ' - print del_tags - delicious.add('luxagraf', 'translinguis#', link.url, link.title, tags = del_tags, extended = link.description, dt =safestr(link.pub_date), replace="no") + delicious.add(settings.DELICIOUS_USER, settings.DELICIOUS_PASS, link.url, link.title, tags = del_tags, extended = striptags(link.description), dt =safestr(link.pub_date), replace="no") def copy_file(url, id): - g = URLGrabber() - filename="/home2/luxagraf/webapps/images/magnolia_thumbs/%s/%s.jpg" %(datetime.datetime.today().strftime("%b").lower(), id) - local_filename = g.urlgrab(url, filename) - return id \ No newline at end of file + filename="/home/luxagraf/webapps/static/images/magnolia_thumbs/%s/%s.jpg" %(datetime.datetime.today().strftime("%b").lower(), id) + urllib.urlretrieve(url, filename) + return id + +def post_to_tumblr(link): + from links import tumblr + blog = settings.TUMBLR_URL + user= settings.TUMBLR_USER + password = settings.TUMBLR_PASSWORD + api = tumblr.Api(blog,user,password) + post = api.write_link(link.url,name=link.title,description=link.description,date=safestr(link.pub_date)) + +def send_to_deliciousfb(link): + """Wanted my links to go to Facebook and since the Facebook API is needlessly complex + I just created a second delicious account and added the feed via facebook""" + del_tags = '' + tags = link.tags.split(',') + for tag in tags: + del_tags += tag.strip().replace(' ','_')+' ' + desc = link.description.replace('
','\n“') + desc = desc.replace('
','”\n') + desc = striptags(desc) + delicious.add(settings.DELTOFACEUSER, settings.DELTOFACEPASS, link.url, link.title, tags = del_tags, extended = desc, dt =safestr(link.pub_date), replace="no") diff --git a/base_urls.py b/base_urls.py index 95e4b8f..6e9e4ca 100644 --- a/base_urls.py +++ b/base_urls.py @@ -5,8 +5,9 @@ from django.views.generic.simple import redirect_to,direct_to_template from django.contrib.sitemaps import FlatPageSitemap from django.conf import settings -from blog.models import BlogSitemap +from blog.models import BlogSitemap,LatestFull from locations.models import WritingbyLocationSitemap +from links.models import LatestLinks admin.autodiscover() @@ -16,6 +17,10 @@ sitemaps = { 'flatpages': FlatPageSitemap, #'locations': WritingbyLocationSitemap } +feeds = { + 'writing': LatestFull, + 'fblinks' : LatestLinks, +} # old page redirects @@ -32,6 +37,7 @@ if settings.DEVELOPMENT: ) urlpatterns += patterns('', + (r'^feeds/(?P.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}), (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/(.*)', admin.site.root), (r'^robots.txt$', direct_to_template, {'template': 'archives/robots.html'}), diff --git a/templates/feeds/blog_description.html b/templates/feeds/blog_description.html new file mode 100644 index 0000000..29c4f44 --- /dev/null +++ b/templates/feeds/blog_description.html @@ -0,0 +1 @@ +{{obj.body_html|safe}} \ No newline at end of file diff --git a/templates/feeds/links_description.html b/templates/feeds/links_description.html new file mode 100644 index 0000000..09d5411 --- /dev/null +++ b/templates/feeds/links_description.html @@ -0,0 +1 @@ +{{obj.description|safe }} \ No newline at end of file -- cgit v1.2.3-70-g09d2