From: Joseph Spiros Date: Mon, 28 Feb 2011 11:32:41 +0000 (+0000) Subject: Created basic homepage displaying feeds and entries. X-Git-Url: http://git.ithinksw.org/~jspiros/reader.git/commitdiff_plain/2c563a536795dae3cc7b973595612e245f66f33b?ds=inline;pf=~jspiros Created basic homepage displaying feeds and entries. Registered models with the admin. --- diff --git a/admin.py b/admin.py new file mode 100644 index 0000000..cf89fb6 --- /dev/null +++ b/admin.py @@ -0,0 +1,5 @@ +from .models import Feed, Entry, Subscription +from django.contrib import admin + + +admin.site.register((Feed, Entry, Subscription)) \ No newline at end of file diff --git a/migrations/0003_auto__add_field_feed_link__add_field_entry_link__chg_field_entry_summa.py b/migrations/0003_auto__add_field_feed_link__add_field_entry_link__chg_field_entry_summa.py new file mode 100644 index 0000000..8c3b95c --- /dev/null +++ b/migrations/0003_auto__add_field_feed_link__add_field_entry_link__chg_field_entry_summa.py @@ -0,0 +1,108 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Feed.link' + db.add_column('reader_feed', 'link', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True), keep_default=False) + + # Adding field 'Entry.link' + db.add_column('reader_entry', 'link', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True), keep_default=False) + + # Changing field 'Entry.summary' + db.alter_column('reader_entry', 'summary', self.gf('django.db.models.fields.TextField')(null=True)) + + # Changing field 'Entry.content' + db.alter_column('reader_entry', 'content', self.gf('django.db.models.fields.TextField')(null=True)) + + + def backwards(self, orm): + + # Deleting field 'Feed.link' + db.delete_column('reader_feed', 'link') + + # Deleting field 'Entry.link' + db.delete_column('reader_entry', 'link') + + # Changing field 'Entry.summary' + db.alter_column('reader_entry', 'summary', self.gf('django.db.models.fields.TextField')(default='')) + + # Changing field 'Entry.content' + db.alter_column('reader_entry', 'content', self.gf('django.db.models.fields.TextField')(default='')) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'reader.entry': { + 'Meta': {'ordering': "['-published']", 'object_name': 'Entry'}, + 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'feed': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'entries'", 'to': "orm['reader.Feed']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'published': ('django.db.models.fields.DateTimeField', [], {}), + 'summary': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {}), + 'uri': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'reader.feed': { + 'Meta': {'object_name': 'Feed'}, + 'alive': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'etag': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + 'reader.subscription': { + 'Meta': {'object_name': 'Subscription'}, + 'custom_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'feed': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subscriptions'", 'to': "orm['reader.Feed']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reader_subscriptions'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['reader'] diff --git a/models.py b/models.py index 806b3ef..71d559b 100644 --- a/models.py +++ b/models.py @@ -5,20 +5,32 @@ from django.contrib.auth.models import User class Feed(models.Model): url = models.URLField() title = models.CharField(max_length=255) + link = models.URLField(blank=True, null=True) updated = models.DateTimeField() etag = models.CharField(max_length=255, blank=True, null=True) modified = models.DateTimeField(blank=True, null=True) alive = models.BooleanField(default=True) + + def __unicode__(self): + return u'%s <%s>' % (self.title, self.url) class Entry(models.Model): feed = models.ForeignKey(Feed, related_name='entries') uri = models.CharField(max_length=255) title = models.CharField(max_length=255) + link = models.URLField(blank=True, null=True) published = models.DateTimeField() updated = models.DateTimeField() - summary = models.TextField() - content = models.TextField() + summary = models.TextField(blank=True, null=True) + content = models.TextField(blank=True, null=True) + + class Meta: + verbose_name_plural = 'entries' + ordering = ['-published'] + + def __unicode__(self): + return u'%s <%s>' % (self.title, self.feed) class Subscription(models.Model): diff --git a/templates/reader/base.html b/templates/reader/base.html new file mode 100644 index 0000000..f9c5865 --- /dev/null +++ b/templates/reader/base.html @@ -0,0 +1,11 @@ + + + + + {% block title %}Reader{% endblock %} + {% block extrahead %}{% endblock %} + + + {% block body %}{% endblock %} + + diff --git a/templates/reader/home.html b/templates/reader/home.html new file mode 100644 index 0000000..6f1ea32 --- /dev/null +++ b/templates/reader/home.html @@ -0,0 +1,16 @@ +{% extends 'reader/base.html' %} + +{% block title %}{{ block.super }} - Home{% endblock %} + +{% block body %} +

Feeds

+ {% for feed in feeds %} + {% if forloop.first %}{% endif %} + {% endfor %} +

Entries

+ {% for entry in entries %} + {% include 'reader/includes/entry.html' %} + {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/templates/reader/includes/entry.html b/templates/reader/includes/entry.html new file mode 100644 index 0000000..38cc058 --- /dev/null +++ b/templates/reader/includes/entry.html @@ -0,0 +1,8 @@ +
+

{{ entry.title }}

+

Published on {{ entry.published }}. Last updated on {{ entry.updated }}.

+ +
+ {% if entry.summary %}{{ entry.summary|safe }}{% else %}{% if entry.content %}{{ entry.content|safe }}{% endif %}{% endif %} +
+
\ No newline at end of file diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..449c71d --- /dev/null +++ b/urls.py @@ -0,0 +1,7 @@ +from .views import home +from django.conf.urls.defaults import patterns, url + + +urlpatterns = patterns('', + url(r'^$', home, name='reader_home'), +) \ No newline at end of file diff --git a/utils.py b/utils.py index e58dbdb..24dcdf4 100644 --- a/utils.py +++ b/utils.py @@ -3,7 +3,71 @@ import datetime import feedparser -def refresh_feed(feed, save=True): +def _rate_content(content): + if content.type == 'application/xhtml+xml': + return 0 + elif content.type == 'text/html': + return 1 + elif content.type == 'text/plain': + return 2 + else: + return 3 + + +def _choose_content(contents): + limited_contents = [content for content in contents if content.type in ('application/xhtml+xml', 'text/html', 'text/plain')] + limited_contents.sort(key=_rate_content) + return limited_contents[0] if len(limited_contents) > 0 else None + + +def _parse_date(date): + try: + return datetime.datetime(*(date[0:6])) + except: + return None + + +def _add_entry(feed, parsed_entry): + title = parsed_entry.get('title', 'Untitled') + link = parsed_entry.get('link', feed.link) + published = _parse_date(parsed_entry.get('published_parsed', parsed_entry.get('created_parsed', parsed_entry.get('updated_parsed', None)))) + if not published: + published = datetime.datetime.now() + updated = _parse_date(parsed_entry.get('updated_parsed', None)) + if not updated: + updated = published + contents = parsed_entry.get('content', None) + if contents: + content = _choose_content(contents).value + else: + content = None + summary = parsed_entry.get('summary', None) + + if summary or content: + entry, created = feed.entries.get_or_create(uri=parsed_entry.id, defaults={ + 'title': title, + 'link': link, + 'published': published, + 'updated': updated, + 'summary': summary, + 'content': content + }) + if not created: + entry.title = title + entry.link = link + entry.published = published + entry.updated = updated + entry.summary = summary + entry.content = content + entry.save() + + +def _add_entries(feed, parsed): + for parsed_entry in parsed.entries: + _add_entry(feed, parsed_entry) + + +def refresh_feed(feed): if feed.alive: parsed = feedparser.parse(feed.url, etag=feed.etag, modified=(feed.modified.timetuple() if feed.modified else None)) if parsed.get('status', None) == 304: @@ -17,9 +81,16 @@ def refresh_feed(feed, save=True): if parsed.has_key('modified'): feed.modified = datetime.datetime(*(parsed.modified[0:6])) feed.title = parsed.feed.get('title', feed.url) - feed.updated = datetime.datetime(*(parsed.feed.get('updated_parsed', datetime.datetime.now().timetuple())[0:6])) - if save: - feed.save() + feed.updated = _parse_date(parsed.feed.get('updated_parsed', datetime.datetime.now().timetuple())[0:6]) + feed.link = parsed.feed.get('link', feed.url) + + feed.save() + _add_entries(feed, parsed) + + +def refresh_all_feeds(): + for feed in Feed.objects.all(): + refresh_feed(feed) def add_feed(url): diff --git a/views.py b/views.py new file mode 100644 index 0000000..0a1679b --- /dev/null +++ b/views.py @@ -0,0 +1,10 @@ +from .models import Feed, Entry +from django.shortcuts import render_to_response +from django.template import RequestContext + + +def home(request): + return render_to_response('reader/home.html', { + 'entries': Entry.objects.all(), + 'feeds': Feed.objects.all(), + }, context_instance=RequestContext(request)) \ No newline at end of file