Compare commits

..

14 Commits

Author SHA1 Message Date
James Turk
8d1492f9b3 updates 2015-06-16 17:19:53 -04:00
James Turk
05dee45ee8 Update README.rst 2015-03-03 17:46:25 -05:00
James Turk
60aedff443 bump for 0.2.1 2009-12-09 14:22:56 -05:00
James Turk
0863c41293 prevent empty ideas (if only it were that easy) 2009-08-31 17:04:00 -04:00
James Turk
07921966b8 Merge branch 'master' of git@github.com:sunlightlabs/django-brainstorm 2009-08-25 16:19:09 -04:00
James Turk
4d1c1ce952 check if Idea is registered with gatekeeper 2009-08-25 16:18:01 -04:00
James Turk
3288c2af78 fix reverse subsite 2009-08-19 14:04:10 -04:00
James Turk
ca6062f5af working with the new labs site 2009-08-19 13:49:19 -04:00
James Turk
f3cfc80f15 still very much a work in progress 2009-08-19 13:04:48 -04:00
James Turk
ef03c3fa74 fix extra query for user_vote 2009-08-19 12:57:16 -04:00
James Turk
cda56dba20 post_save imported 2009-08-19 12:25:35 -04:00
James Turk
104cf2a32a attempt to merge brainstorm with what was anthill.ideas 2009-08-19 12:18:57 -04:00
James Turk
c91028d840 Revert "htmlize"
This reverts commit a1b7f2d9fa.
2009-06-26 14:15:51 -04:00
James Turk
a1b7f2d9fa htmlize 2009-06-26 14:06:15 -04:00
10 changed files with 123 additions and 94 deletions

14
CHANGELOG Normal file
View File

@ -0,0 +1,14 @@
0.2.1 - December 9 2009
=======================
- disallow submission of empty ideas
- fix a packaging bug
0.2.0 - August 25 2009
======================
- rewrite for Sunlight Labs website
- reduced a few extra queries
- integrate with django-gatekeeper if installed
0.1.0 - May 27 2009
=====
- initial working release, focused on subsites

View File

@ -1,3 +1,4 @@
Copyright (c) 2015, James Turk
Copyright (c) 2009, Sunlight Foundation
All rights reserved.
@ -5,6 +6,5 @@ Redistribution and use in source and binary forms, with or without modification,
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Sunlight Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,4 +1,5 @@
include LICENSE
include CHANGELOG
include README.rst
include *.py
recursive-include brainstorm/templates *

View File

@ -4,21 +4,14 @@ django-brainstorm
Django app for creating a site with multiple areas to brainstorm ideas.
This app powers http://feedback.sunlightfoundation.com/hackathon/ and http://feedback.sunlightfoundation.com/oogl/ and makes it easy to create any number of these 'subsites.'
django-brainstorm is a project of Sunlight Labs (c) 2009.
Written by James Turk <jturk@sunlightfoundation.com>.
All code is under a BSD-style license, see LICENSE for details.
Source: http://github.com/sunlightlabs/django-brainstorm/
Source: http://github.com/jamesturk/django-brainstorm/
Requirements
============
python >= 2.4
django >= 1.0
* python >= 2.4
* django >= 1.0
Usage
=====

View File

@ -0,0 +1 @@
__version__ = '0.2.1'

View File

@ -3,25 +3,29 @@ from brainstorm.models import Subsite
class SubsiteFeed(Feed):
description_template = 'feedback/idea_rss_description.html'
title_template = 'brainstorm/feed_title.html'
description_template = 'brainstorm/feed_description.html'
def get_object(self, bits):
return Subsite.objects.get(slug__exact=bits[0])
def title(self, obj):
return '%s' % obj.name
return 'Latest ideas submitted for %s' % obj.name
def description(self, obj):
return 'Latest ideas submitted for %s' % obj.name
def link(self, obj):
if not obj:
raise FeedDoesNotExist
return obj.get_absolute_url()
def description(self, obj):
return 'Latest ideas submitted for %s' % obj.name
def items(self, obj):
return obj.ideas.order_by('-submit_date')[:30]
def item_link(self, item):
return item.get_absolute_url()
def item_author_name(self, item):
return item.user

View File

@ -3,7 +3,7 @@ from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.contrib.contenttypes import generic
from django.contrib.comments.models import Comment
import secretballot
from django.db.models.signals import post_save
ALLOW_ALL, REQUIRE_LOGIN, DISALLOW_ALL = range(3)
SUBSITE_POST_STATUS = (
@ -26,7 +26,7 @@ class Subsite(models.Model):
return self.name
def get_absolute_url(self):
return reverse('subsite', args=[self.slug])
return reverse('ideas_popular', args=[self.slug])
def user_can_post(self, user):
if self.post_status == DISALLOW_ALL:
@ -36,10 +36,16 @@ class Subsite(models.Model):
elif self.post_status == REQUIRE_LOGIN:
return not user.is_anonymous()
class IdeaManager(models.Manager):
def with_user_vote(self, user):
return self.extra(select={'user_vote':'SELECT value FROM brainstorm_vote WHERE idea_id=brainstorm_idea.id AND user_id=%s'}, select_params=[user.id])
class Idea(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
score = models.IntegerField(default=0)
submit_date = models.DateTimeField(auto_now_add=True)
@ -48,10 +54,28 @@ class Idea(models.Model):
comments = generic.GenericRelation(Comment, object_id_field='object_pk')
objects = IdeaManager()
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('idea', args=[self.subsite_id, self.id])
return reverse('idea_detail', args=[self.subsite_id, self.id])
secretballot.enable_voting_on(Idea)
class Vote(models.Model):
user = models.ForeignKey(User, related_name='idea_votes')
idea = models.ForeignKey(Idea, related_name='votes')
value = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return '%s %s on %s' % (self.user, self.value, self.idea)
class Meta:
unique_together = (('user', 'idea'),)
def update_idea_votes(sender, instance, created, **kwargs):
score = instance.idea.votes.aggregate(score=models.Sum('value'))['score']
instance.idea.score = score
instance.idea.save()
post_save.connect(update_idea_votes, sender=Vote)

View File

@ -1,8 +1,11 @@
from django.conf.urls.defaults import *
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from brainstorm.models import Idea
from brainstorm.feeds import SubsiteFeed
BRAINSTORM_USE_SECRETBALLOT = getattr(settings, 'BRAINSTORM_USE_SECRETBALLOT', False)
feeds = {
'latest': SubsiteFeed,
}
@ -14,13 +17,14 @@ urlpatterns = patterns('',
)
urlpatterns += patterns('brainstorm.views',
url(r'^submit_comment/$', 'submit_comment', name='submit_idea_comment'),
url(r'^(?P<slug>[\w-]+)/$', 'idea_list', {'ordering': 'most_popular'}, name='subsite'),
url(r'^(?P<slug>[\w-]+)/latest/$', 'idea_list', {'ordering': 'latest'}, name='subsite_latest'),
url(r'^(?P<slug>[\w-]+)/(?P<id>\d+)/$', 'idea_detail', name='idea'),
url(r'^(?P<slug>[\w-]+)/$', 'idea_list', {'ordering': 'most_popular'}, name='ideas_popular'),
url(r'^(?P<slug>[\w-]+)/latest/$', 'idea_list', {'ordering': 'latest'}, name='ideas_latest'),
url(r'^(?P<slug>[\w-]+)/(?P<id>\d+)/$', 'idea_detail', name='idea_detail'),
url(r'^(?P<slug>[\w-]+)/new_idea/$', 'new_idea', name='new_idea'),
url(r'^vote/$', 'vote', name='idea_vote'),
)
if BRAINSTORM_USE_SECRETBALLOT:
urlpatterns = patterns('secretballot.views',
url(r'^vote_up/(?P<object_id>\d+)/$', 'vote',
{'content_type': ContentType.objects.get_for_model(Idea), 'vote': 1},

View File

@ -1,77 +1,65 @@
import datetime
from django.template import RequestContext
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render_to_response, redirect
from django.http import HttpResponse
from django.contrib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType
from django.views.generic import list_detail
from django.views.decorators.http import require_POST
from brainstorm.models import Subsite, Idea
from django.contrib.auth.decorators import login_required
from django.conf import settings
from brainstorm.models import Subsite, Idea, Vote
def idea_list(request, slug, ordering='-total_upvotes'):
subsite = get_object_or_404(Subsite, pk=slug)
ordering_db = {'most_popular': '-total_upvotes',
ordering_db = {'most_popular': '-score',
'latest': '-submit_date'}[ordering]
paginator = Paginator(Idea.objects.from_request(request).filter(subsite=subsite).order_by(ordering_db),
subsite.ideas_per_page)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
ideas = paginator.page(page)
except (EmptyPage, InvalidPage):
ideas = paginator.page(paginator.num_pages)
return render_to_response('brainstorm/index.html',
{'subsite':subsite, 'ideas': ideas,
'ordering': ordering,
'user_can_post': subsite.user_can_post(request.user)},
context_instance=RequestContext(request))
qs = Idea.objects.with_user_vote(request.user).filter(subsite__slug=slug).select_related().order_by(ordering_db)
if hasattr(qs, '_gatekeeper'):
qs = qs.approved()
return list_detail.object_list(request, queryset=qs,
extra_context={'ordering': ordering, 'subsite':slug}, paginate_by=10,
template_object_name='idea')
def idea_detail(request, slug, id):
subsite = get_object_or_404(Subsite, pk=slug)
idea = get_object_or_404(Idea.objects.from_request(request),
subsite=slug, pk=id)
return render_to_response('brainstorm/idea.html',
{'subsite':subsite, 'idea': idea,
'user_can_post': subsite.user_can_post(request.user)},
idea = get_object_or_404(Idea.objects.with_user_vote(request.user), pk=id, subsite__slug=slug)
return render_to_response('brainstorm/idea_detail.html',
{'idea': idea},
context_instance=RequestContext(request))
@require_POST
def new_idea(request, slug):
subsite = get_object_or_404(Subsite, pk=slug)
if not subsite.user_can_post(request.user):
return HttpResponseRedirect(subsite.get_absolute_url())
return redirect(subsite.get_absolute_url())
title = request.POST['title']
description = request.POST['description']
if request.user.is_anonymous():
user = None
else:
if not title.strip() or not description.strip():
return redirect(subsite.get_absolute_url())
user = request.user
idea = Idea.objects.create(title=title, description=description,
user=user, subsite=subsite)
return HttpResponseRedirect(idea.get_absolute_url())
idea = Idea.objects.create(title=title, description=description, user=user,
subsite=subsite)
return redirect(idea)
@require_POST
def submit_comment(request):
from django.conf import settings
content_type = ContentType.objects.get_for_model(Idea).id
site = settings.SITE_ID
object_pk = request.POST['idea_id']
name = request.POST.get('name', 'anonymous')
email = request.POST.get('email', '')
url = request.POST.get('url', '')
comment = request.POST['comment']
date = datetime.datetime.now()
ip = request.META['REMOTE_ADDR']
c = Comment.objects.create(user_name=name, user_email=email, user_url=url,
comment=comment, submit_date=date, ip_address=ip,
site_id=site, content_type_id=content_type, object_pk=object_pk)
idea = Idea.objects.get(pk=object_pk)
linkback = '%s#c%s' % (idea.get_absolute_url(), c.id)
return HttpResponseRedirect(linkback)
@login_required
def vote(request):
idea_id = int(request.POST.get('idea'))
score = int(request.POST.get('score'))
if score not in (0,1):
score = 0
idea = get_object_or_404(Idea, pk=idea_id)
score_diff = score
vote, created = Vote.objects.get_or_create(user=request.user, idea=idea,
defaults={'value':score})
if not created:
new_score = idea.score + (score-vote.value)
vote.value = score
vote.save()
else:
new_score = idea.score
if request.is_ajax():
return HttpResponse("{'score':%d}" % new_score)
return redirect(idea)

View File

@ -4,15 +4,15 @@ long_description = open('README.rst').read()
setup(
name='django-brainstorm',
version="0.1",
version="0.2.1",
package_dir={'brainstorm': 'brainstorm'},
packages=['brainstorm'],
package_data={'brainstorm': ['templates/brainstorm/*.html']},
description='Django brainstorming site',
author='James Turk',
author_email='jturk@sunlightfoundation.com',
author_email='james.p.turk@gmail.com',
license='BSD License',
url='http://github.com/sunlightlabs/django-brainstorm/',
url='http://github.com/jamesturk/django-brainstorm/',
long_description=long_description,
platforms=["any"],
classifiers=[