Skip to content

Commit

Permalink
Merge pull request #37 from matagus/feature-add-feed
Browse files Browse the repository at this point in the history
Allowing users to add a feed.
  • Loading branch information
matagus committed Apr 19, 2015
2 parents c9dcf21 + 8c75aeb commit 32eaf35
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 6 deletions.
2 changes: 1 addition & 1 deletion planet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
VERSION = (0, 6, 2, "f") # following PEP 386
VERSION = (0, 6, 3, "f") # following PEP 386

def get_version():
version = "%s.%s" % (VERSION[0], VERSION[1])
Expand Down
4 changes: 2 additions & 2 deletions planet/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django import forms
from django.utils.translation import ugettext as _


SEARCH_CHOICES = (
("posts", _("Posts")),
("tags", _("Tags")),
Expand All @@ -13,8 +14,7 @@
("feeds", _("Feeds")),
)


class SearchForm(forms.Form):
w = forms.ChoiceField(choices=SEARCH_CHOICES, label="")
q = forms.CharField(max_length=100, label="")


6 changes: 4 additions & 2 deletions planet/management/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
Author, PostAuthorData, Enclosure, Category)
from planet.signals import post_created


class PostAlreadyExists(Exception):
pass

def process_feed(feed_url, create=False, category_title=None):

def process_feed(feed_url, owner=None, create=False, category_title=None):
"""
Stores a feed, its related data, its entries and their related data.
If create=True then it creates the feed, otherwise it only stores new
Expand Down Expand Up @@ -115,7 +117,7 @@ def normalize_tag(tag):
blog_url = link[0]["href"]

blog, created = Blog.objects.get_or_create(
url=blog_url, defaults={"title": title})
url=blog_url, defaults={"title": title}, owner=owner)

generator_dict = document.feed.get("generator_detail", {})

Expand Down
162 changes: 162 additions & 0 deletions planet/migrations/0010_add_field_blog_owner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as 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 'Blog.owner'
db.add_column(u'planet_blog', 'owner',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True),
keep_default=False)


def backwards(self, orm):
# Deleting field 'Blog.owner'
db.delete_column(u'planet_blog', 'owner_id')


models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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'})
},
u'planet.author': {
'Meta': {'ordering': "(u'name', u'email')", 'object_name': 'Author'},
'email': ('django.db.models.fields.EmailField', [], {'db_index': 'True', 'max_length': '75', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'profile_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
u'planet.blog': {
'Meta': {'ordering': "(u'title', u'url')", 'object_name': 'Blog'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200', 'db_index': 'True'})
},
u'planet.category': {
'Meta': {'ordering': "(u'title', u'date_created')", 'object_name': 'Category'},
'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
},
u'planet.enclosure': {
'Meta': {'ordering': "(u'post', u'mime_type', u'link')", 'object_name': 'Enclosure'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'length': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'link': ('django.db.models.fields.URLField', [], {'max_length': '500', 'db_index': 'True'}),
'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'post': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Post']"})
},
u'planet.feed': {
'Meta': {'ordering': "(u'title',)", 'object_name': 'Feed'},
'blog': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Blog']", 'null': 'True', 'blank': 'True'}),
'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Category']", 'null': 'True', 'blank': 'True'}),
'etag': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
'generator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Generator']", 'null': 'True', 'blank': 'True'}),
'guid': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '32', 'null': 'True', 'blank': 'True'}),
'icon_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'image_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'info': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'last_checked': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_modified': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'rights': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['sites.Site']", 'null': 'True', 'blank': 'True'}),
'subtitle': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200', 'db_index': 'True'})
},
u'planet.feedlink': {
'Meta': {'ordering': "(u'feed', u'rel', u'mime_type')", 'object_name': 'FeedLink'},
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Feed']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'link': ('django.db.models.fields.URLField', [], {'max_length': '500', 'db_index': 'True'}),
'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'rel': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
},
u'planet.generator': {
'Meta': {'ordering': "(u'name', u'version')", 'unique_together': "((u'name', u'link', u'version'),)", 'object_name': 'Generator'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'link': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'version': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
u'planet.post': {
'Meta': {'ordering': "(u'-date_created', u'-date_modified')", 'unique_together': "((u'feed', u'guid'),)", 'object_name': 'Post'},
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['planet.Author']", 'through': u"orm['planet.PostAuthorData']", 'symmetrical': 'False'}),
'comments_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'content': ('django.db.models.fields.TextField', [], {}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Feed']"}),
'guid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '1000', 'db_index': 'True'})
},
u'planet.postauthordata': {
'Meta': {'ordering': "(u'author', u'post', u'is_contributor')", 'object_name': 'PostAuthorData'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Author']"}),
'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_contributor': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'post': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Post']"})
},
u'planet.postlink': {
'Meta': {'ordering': "(u'post', u'title', u'rel')", 'object_name': 'PostLink'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'link': ('django.db.models.fields.URLField', [], {'max_length': '500', 'db_index': 'True'}),
'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'post': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['planet.Post']"}),
'rel': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'sites.site': {
'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}

complete_apps = ['planet']
16 changes: 16 additions & 0 deletions planet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import feedparser
from datetime import datetime
from time import mktime, struct_time

from django.db import models
from django.utils.translation import ugettext_lazy as _
Expand All @@ -32,15 +33,26 @@
EnclosureManager)


def _get_user_model():
try:
# New sinc Dajngo 1.5
return settings.AUTH_USER_MODEL
except AttributeError:
# Django < 1.5
return "auth.User"


@python_2_unicode_compatible
class Blog(models.Model):
"""
A model to store primary info about a blog or website that which feed or
feeds are aggregated to our planet
"""

title = models.CharField(_("title"), max_length=255, blank=True, db_index=True)
url = models.URLField(_("Url"), unique=True, db_index=True)
date_created = models.DateTimeField(_("Date created"), auto_now_add=True)
owner = models.ForeignKey(_get_user_model(), null=True, blank=True)

site_objects = BlogManager()
objects = models.Manager()
Expand Down Expand Up @@ -185,7 +197,10 @@ def save(self, *args, **kwargs):
self.icon_url = document.feed.get("icon")
self.language = document.feed.get("language")
self.etag = document.get("etag", '')

self.last_modified = document.get("updated_parsed", datetime.now())
if isinstance(self.last_modified, struct_time):
self.last_modified = datetime.fromtimestamp(mktime(self.last_modified))

self.blog, created = Blog.objects.get_or_create(
url=blog_url, defaults={"title": self.title})
Expand All @@ -199,6 +214,7 @@ def save(self, *args, **kwargs):
version=generator_dict.get("version"))
else:
self.generator = None

super(Feed, self).save(*args, **kwargs)

def __str__(self):
Expand Down
1 change: 1 addition & 0 deletions planet/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<li><a href="{% url 'planet.views.blogs_list' %}">Blogs</a></li>
<li><a href="{% url 'planet.views.authors_list' %}">Authors</a></li>
<li><a href="{% url 'planet.views.tags_cloud' %}">Tags</a></li>
<li><a href="{% url 'planet_feed_add' %}">Add Feed</a></li>
</ul>
{% endblock %}
</div>
Expand Down
29 changes: 29 additions & 0 deletions planet/templates/planet/feeds/add.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% extends "planet/base.html" %}
{% load i18n %}
{% load url from future %}

{% block head_title %}{% trans "Add a New Feed" %}{% endblock %}

{% block extra_head %}
<meta name="title" content="{% trans 'Add a New Feed' %}" />
<meta name="description" content="{% trans 'Add a New Feed' %}" />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="http://{{ site.domain }}{{ request.path }}"/>
{% endblock %}

{% block breadcrumb_section %}
<li><a href="{% url 'planet.views.feeds_list' %}">{% trans 'Feeds' %}</a> <span class="divider">/</span></li>
{% endblock %}

{% block breadcrumb_detail %}
<li class="active">{% trans 'Add a New Feed' %}</li>
{% endblock %}

{% block content %}
<h1>{% trans "Add a New Feed" %}</h1>
<form action="" method="POST">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans 'Submit' %}" />
<a href="javascript: history.back();">{% trans 'Cancel' %}</a>
</form>
{% endblock %}
1 change: 1 addition & 0 deletions planet/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Meta:

title = factory.Sequence(lambda n: u'blog-{}'.format(n))
url = factory.LazyAttribute(lambda obj: u'http://{}.blogspot.com/'.format(obj.title))
owner = None


class GeneratorFactory(factory.DjangoModelFactory):
Expand Down
4 changes: 3 additions & 1 deletion planet/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
try:
# Django 1.6
from django.conf.urls import patterns, url, include
from django.conf.urls import patterns, url
except ImportError:
# Django < 1.6
from django.conf.urls.defaults import patterns, url
Expand All @@ -11,13 +11,15 @@

from planet.feeds import PostFeed, AuthorFeed, AuthorTagFeed, TagFeed
from planet.sitemaps import planet_sitemaps_dict
from planet.views import FeedAddView


urlpatterns = patterns('planet.views',
url(r'^blogs/(?P<blog_id>\d+)/(?P<slug>[a-zA-Z0-9_\-]+)/$', "blog_detail", name="planet_blog_detail"),
url(r'^blogs/(?P<blog_id>\d+)/$', "blog_detail"),
url(r'^blogs/$', "blogs_list", name="planet_blog_list"),

url(r'^feeds/add/$', FeedAddView.as_view(), name="planet_feed_add"),
url(r'^feeds/(?P<feed_id>\d+)/(?P<slug>[a-zA-Z0-9_\-]+)/tags/(?P<tag>.*)/$', "feed_detail", name="planet_by_tag_feed_detail"),
url(r'^feeds/(?P<feed_id>\d+)/(?P<slug>[a-zA-Z0-9_\-]+)/$', "feed_detail", name="planet_feed_detail"),
url(r'^feeds/(?P<feed_id>\d+)/$', "feed_detail"),
Expand Down

0 comments on commit 32eaf35

Please sign in to comment.