Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott Robinson authored and Scott Robinson committed Oct 10, 2014
0 parents commit 1a266b5
Show file tree
Hide file tree
Showing 13 changed files with 768 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
@@ -0,0 +1,14 @@
*.pyc
*~
.DS_Store
.AppleDouble
*.swp
*.egg-info
*.egg
*.EGG
*.EGG-INFO
bin
build
develop-eggs
.hg
.svn
1 change: 1 addition & 0 deletions AUTHORS
@@ -0,0 +1 @@
The primary author of django-voting is Scott Robinson.
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
Copyright (c) 2014 Scott Robinson

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
96 changes: 96 additions & 0 deletions README.md
@@ -0,0 +1,96 @@
Django-Voting
=============

Django-Voting is a basic app for tracking the number of votes placed for
any object in the database. It provides the ability to upvote and downvote.

This project is based on the [django-hitcount][1] by Damon Timm.

TODO
----

Most of the basic functionality you'd need for a simple voting system has
been implemented. Possible additions are:

- Better JavaScript support for ajax calls
- Better UI/utilities for the admin site
- Rate-limiting to avoid vote-spamming
- Options for allowing users to vote multiple times on an object

Installation:
-------------

Simplest way to formally install is to run:

./setup.py install

Or, you could do a PIP installation:

pip install -e git://github.com/scottwrobinson/django-voting.git#egg=django-voting

Or, you can link the source to your `site-packages` directory. This is useful
if you plan on pulling future changes and don't want to keep running
`./setup.py install`.

cd ~/src
git clone https://github.com/scottwrobinson/django-voting.git
sudo ln -s `pwd`/django-voting/voting `python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`/voting


Then modify your settings.py, adding the package voting in INSTALLED_APPS

INSTALLED_APPS = (
'...',
'voting',
)


You need to add one line to your urls.py file.
----------------------------------------------
urlpatterns = patterns('',
url(r'^site/ajax/vote$', # you can change this url if you would like
update_vote_count_ajax,
name='votecount_update_ajax'), # keep this name the same


Ajax Call
---------
The ajax call to the `update_vote_count_ajax` view requires two variables:

- direction
The vote direction. +1 for upvote and -1 for downvote.
- votecount_pk
The pk of the object to be voted on. Can be retrieved using the {% get_vote_object_pk for [object] %} tag (see below).
- csrfmiddlewaretoken (optional)
The CSRF token. Only required if CSRF validation is enabled.

The ajax view returns two variables back:

- status
"success" for successful votes and "failed" for votes that did not get recorded.
- net_change
The net change of the object's vote total.

Custom Template Tags
--------------------
Don't forget to load the custom tags with: `{% load voting_tags %}`

- Return total votes for an object:
{% get_vote_count for [object] %}

- Get total votes for an object as a specified variable:
{% get_vote_count for [object] as [var] %}

- Get total votes for an object over a certain time period:
{% get_vote_count for [object] within ["days=1,minutes=30"] %}

- Get total votes for an object over a certain time period as a variable:
{% get_vote_count for [object] within ["days=1,minutes=30"] as [var] %}

- Get or create the pk for the given object:
{% get_vote_object_pk for [object] %}

- Get or create the pk for the given object as a specified variable:
{% get_vote_object_pk for [object] as [var] %}

[1]:https://github.com/thornomad/django-hitcount
28 changes: 28 additions & 0 deletions setup.py
@@ -0,0 +1,28 @@
import os
from setuptools import setup, find_packages

def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()

setup(
name = "django-voting",
version = "0.1",
url = 'http://github.com/scottwrobinson/django-voting',
license = 'BSD',
description = "Django voting application that tracks the number of votes for any DB objects.",
long_description = read('README.md'),

author = 'Scott Robinson',

packages = find_packages(),

classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Plugins',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
)
12 changes: 12 additions & 0 deletions voting/__init__.py
@@ -0,0 +1,12 @@
VERSION = (0, 1, 0, 'beta', 1)

def get_version():
version = '%s.%s' % (VERSION[0], VERSION[1])
if VERSION[2]:
version = '%s.%s' % (version, VERSION[2])
else:
if VERSION[3] != 'final':
version = '%s %s' % (version, VERSION[3])
return version

__version__ = get_version()
22 changes: 22 additions & 0 deletions voting/actions.py
@@ -0,0 +1,22 @@
from django.core.exceptions import PermissionDenied

def delete_queryset(modeladmin, request, queryset):
# TODO
#
# Right now, when you delete a vote there is no warning or "turing back".
# Consider adding a "are you sure you want to do this?" as is
# implemented in django's contrib.admin.actions file.

if not modeladmin.has_delete_permission(request):
raise PermissionDenied
else:
if queryset.count() == 1:
msg = "1 vote was"
else:
msg = "%s votes were" % queryset.count()

for obj in queryset.iterator():
obj.delete() # calling it this way to get custom delete() method

modeladmin.message_user(request, "%s successfully deleted." % msg)
delete_queryset.short_description = "DELETE selected votes"
57 changes: 57 additions & 0 deletions voting/admin.py
@@ -0,0 +1,57 @@
from django.contrib import admin

from voting.models import Vote, VoteCount
from voting import actions

def created_format(obj):
'''
Format the created time for the admin. PS: I am not happy with this.
'''
return "%s" % obj.date_created.strftime("%m/%d/%y<br />%H:%M:%S")
created_format.short_description = "Date (UTC)"
created_format.allow_tags = True
created_format.admin_order_field = 'date_created'


class VoteAdmin(admin.ModelAdmin):
list_display = (created_format,'user','ip','votecount')
search_fields = ('ip',)
date_hierarchy = 'date_created'
actions = [ actions.delete_queryset,
]

def __init__(self, *args, **kwargs):
super(VoteAdmin, self).__init__(*args, **kwargs)
self.list_display_links = (None,)

def get_actions(self, request):
# Override the default `get_actions` to ensure that our model's
# `delete()` method is called.
actions = super(VoteAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions

# TODO: Add inlines to the VoteCount object so we can see a list of the recent
# votes for the object. For this inline to work, we need to:
# a) be able to see the vote data but *not* edit it
# b) have the `delete` command actually alter the VoteCount
# c) remove the ability to 'add new vote'
#
#class VoteInline(admin.TabularInline):
# model = Vote
# fk_name = 'votecount'
# extra = 0

class VoteCountAdmin(admin.ModelAdmin):
list_display = ('content_object','vote_sum','modified')
fields = ('upvotes', 'downvotes')

# TODO: Another option
#The fields option, unlike list_display, may only contain names of fields on the model or the form specified by form. It may contain callables only if they are listed in readonly_fields.

# TODO - when above is ready
#inlines = [ VoteInline, ]

admin.site.register(Vote, VoteAdmin)
admin.site.register(VoteCount, VoteCountAdmin)

0 comments on commit 1a266b5

Please sign in to comment.