PKEG`# # wagtail_polls/settings.py""" Django settings for wagtail_polls project. Generated by 'django-admin startproject' using Django 1.8.4. For more information on this file, see https://docs.djangoproject.com/en/1.8/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.8/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'ulfb+9f96dcx(3wy4^d25cy*9vcg%itmp_e&pds1lbf=&00e1v' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'compressor', 'taggit', 'modelcluster', 'rest_framework', 'wagtail.wagtailcore', 'wagtail.wagtailadmin', 'wagtail.wagtaildocs', 'wagtail.wagtailsnippets', 'wagtail.wagtailusers', 'wagtail.wagtailimages', 'wagtail.wagtailembeds', 'wagtail.wagtailsearch', 'wagtail.wagtailsites', 'wagtail.wagtailredirects', 'wagtail.wagtailforms', 'polls', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.wagtailcore.middleware.SiteMiddleware', 'wagtail.wagtailredirects.middleware.RedirectMiddleware', ) ROOT_URLCONF = 'wagtail_polls.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'wagtail_polls.wsgi.application' WAGTAIL_SITE_NAME = 'Wagtail Polls Demo' # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = 'static' PKEGd"ccwagtail_polls/urls.pyfrom django.conf.urls import include, url from django.contrib import admin from wagtail.wagtailcore import urls as wagtail_urls from wagtail.wagtailadmin import urls as wagtailadmin_urls urlpatterns = [ url(r'^django-admin/', include(admin.site.urls)), url(r'^admin/', include(wagtailadmin_urls)), url(r'^polls/', include('polls.urls')), ] PKREG}twagtail_polls/wsgi.py""" WSGI config for wagtail_polls project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ """ import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wagtail_polls.settings") application = get_wsgi_application() PKREGwagtail_polls/__init__.pyPK)IG?riVVpolls/models.pyfrom django.db import models from django.conf import settings from django.core.exceptions import ValidationError from django.utils.translation import gettext as _ from django.utils.html import strip_tags from wagtail.wagtailsnippets.models import register_snippet from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel from wagtail.wagtailcore.models import Orderable from wagtail.wagtailcore.fields import RichTextField from modelcluster.models import ClusterableModel from modelcluster.fields import ParentalKey @register_snippet class Poll(ClusterableModel, models.Model): name = models.CharField(max_length=200) message = RichTextField( blank=True, help_text=_("Copy to show on the poll. For example a question.")) panels = [ FieldPanel('name'), FieldPanel('message'), InlinePanel('pollitem_set', label=_("Choices")), ] def __str__(self): return self.name class PollItem(Orderable, models.Model): poll = ParentalKey(Poll) message = RichTextField( blank=True, verbose_name=_("Choice"), ) panels = [ FieldPanel('message'), ] def __str__(self): return strip_tags(self.message) class Vote(models.Model): item = models.ForeignKey(PollItem) ip = models.GenericIPAddressField() user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True) datetime = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('user', 'item') def __str__(self): return '{} {}'.format(self.user, self.item) def validate_unique(self, *args, **kwargs): super().validate_unique(*args, **kwargs) qs = Vote.objects.filter(user=self.user, item__poll=self.item.poll) if qs.exists(): raise ValidationError({'item': ['Item and user must be unique']}) PKZGB0ddpolls/views.pyfrom rest_framework import viewsets, permissions, mixins from rest_framework.exceptions import ValidationError from .models import Poll, Vote from .serializers import PollSerializer, VoteSerializer class PollViewSet(viewsets.ModelViewSet): """ Informational use intended. Not great for editing. """ queryset = Poll.objects.all() serializer_class = PollSerializer permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly] class VoteViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): """ Submit a vote. Must be logged in. User and ip address will be inferred. """ queryset = Vote.objects.all() serializer_class = VoteSerializer permission_classes = [permissions.IsAuthenticated] def perform_create(self, serializer): request = self.request poll = Poll.objects.filter(pollitem__id=serializer.data['item']).first() votes = Vote.objects.filter( user=request.user, item__poll=poll, ) if votes.exists(): raise ValidationError("You have already voted.") x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0] else: ip = request.META.get('REMOTE_ADDR') serializer.save( user=request.user, ip=ip, ) PKbEGc##polls/tests.pyfrom django.core.urlresolvers import reverse from django.contrib.auth.models import User from rest_framework.test import APITestCase from .models import Poll, PollItem, Vote class TestPolls(APITestCase): def create_poll(self): return Poll.objects.create(name="test", message="testing") def test_polls_list(self): url = reverse('polls-list') poll = self.create_poll() res = self.client.get(url) self.assertContains(res, poll.name) def test_vote(self): url = reverse('vote-list') poll = self.create_poll() item = PollItem.objects.create(poll=poll, message="1") user = User.objects.create_user( username="test", email="test@example.com", password="test") self.client.force_authenticate(user=user) data = {'item': item.id} res = self.client.post(url, data) self.assertEqual(res.status_code, 201) votes = Vote.objects.filter(user=user) self.assertEqual(votes.count(), 1) # One vote per poll res = self.client.post(url, data) self.assertEqual(res.status_code, 400) votes = Vote.objects.filter(user=user) self.assertEqual(votes.count(), 1) poll2 = Poll.objects.create(name="test2", message="testing") item2 = PollItem.objects.create(poll=poll2, message="2") data = {'item': item2.id} res = self.client.post(url, data) self.assertEqual(res.status_code, 201) votes = Vote.objects.filter(user=user) self.assertEqual(votes.count(), 2) PKLEG,RR polls/urls.pyfrom django.conf.urls import patterns, url, include from rest_framework import routers from . import views router = routers.DefaultRouter() router.register(r'polls', views.PollViewSet, base_name='polls') router.register(r'vote', views.VoteViewSet, base_name='vote') urlpatterns = patterns('', url(r'api/', include(router.urls)), ) PKEG |polls/serializers.pyfrom django.contrib.auth import get_user_model from rest_framework import serializers from .models import Poll, PollItem, Vote User = get_user_model() class PollItemSerializer(serializers.ModelSerializer): class Meta: model = PollItem class PollSerializer(serializers.ModelSerializer): pollitem_set = PollItemSerializer(many=True) class Meta: model = Poll class VoteSerializer(serializers.ModelSerializer): user = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, read_only=True, default=None) class Meta: model = Vote fields = ('user', 'ip', 'item',) read_only_fields = ('user', 'ip') PKѣGpolls/__init__.pyPK0EGN5polls/admin.pyfrom django.contrib import admin from .models import Vote @admin.register(Vote) class VoteAdmin(admin.ModelAdmin): list_display = ['user', 'item', 'ip'] search_fields = ['user__username', 'item__message', 'ip'] PKEG3"+polls/migrations/0002_auto_20151005_1920.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models import wagtail.wagtailcore.fields import datetime class Migration(migrations.Migration): dependencies = [ ('polls', '0001_initial'), ] operations = [ migrations.AlterModelOptions( name='poll', options={}, ), migrations.RemoveField( model_name='poll', name='date', ), migrations.RemoveField( model_name='poll', name='title', ), migrations.RemoveField( model_name='pollitem', name='image', ), migrations.RemoveField( model_name='pollitem', name='value', ), migrations.AddField( model_name='poll', name='message', field=wagtail.wagtailcore.fields.RichTextField(help_text='Copy to show on the poll. For example a question.', blank=True), ), migrations.AddField( model_name='poll', name='name', field=models.CharField(default=datetime.datetime(2015, 10, 5, 19, 20, 13, 884186), max_length=200), preserve_default=False, ), migrations.AddField( model_name='pollitem', name='message', field=wagtail.wagtailcore.fields.RichTextField(verbose_name='Choice', blank=True), ), ] PKѣGq!<. polls/migrations/0001_initial.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import django.db.models.deletion import modelcluster.fields import datetime from django.conf import settings class Migration(migrations.Migration): dependencies = [ ('wagtailimages', '0006_add_verbose_names'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Poll', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('title', models.CharField(max_length=250, verbose_name='question')), ('date', models.DateField(default=datetime.date.today, verbose_name='date')), ], options={ 'ordering': ['-date'], }, ), migrations.CreateModel( name='PollItem', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), ('value', models.CharField(blank=True, max_length=250)), ('image', models.ForeignKey(blank=True, related_name='+', on_delete=django.db.models.deletion.SET_NULL, to='wagtailimages.Image', null=True)), ('poll', modelcluster.fields.ParentalKey(to='polls.Poll')), ], options={ 'abstract': False, 'ordering': ['sort_order'], }, ), migrations.CreateModel( name='Vote', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), ('ip', models.GenericIPAddressField()), ('datetime', models.DateTimeField(auto_now_add=True)), ('item', models.ForeignKey(to='polls.PollItem')), ('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True)), ], ), migrations.AlterUniqueTogether( name='vote', unique_together=set([('user', 'item')]), ), ] PKѣGpolls/migrations/__init__.pyPKZG^- -wagtail_polls-1.0.2.dist-info/DESCRIPTION.rstUNKNOWN PKZGk..+wagtail_polls-1.0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Django", "Programming Language :: Python", "Programming Language :: Python :: 3.4", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License"], "extensions": {"python.details": {"contacts": [{"email": "david@thelabnyc.com", "name": "David Burke", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://gitlab.com/thelabnyc/wagtail_polls"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["django", "wagtail", "poll"], "license": "Apache License", "metadata_version": "2.0", "name": "wagtail-polls", "run_requires": [{"requires": ["wagtail (>=1.0)"]}], "summary": "A small polls app in wagtail", "version": "1.0.2"}PKZGr+wagtail_polls-1.0.2.dist-info/top_level.txtpolls wagtail_polls PKZG}\\#wagtail_polls-1.0.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKZGn3{{&wagtail_polls-1.0.2.dist-info/METADATAMetadata-Version: 2.0 Name: wagtail-polls Version: 1.0.2 Summary: A small polls app in wagtail Home-page: https://gitlab.com/thelabnyc/wagtail_polls Author: David Burke Author-email: david@thelabnyc.com License: Apache License Keywords: django wagtail poll Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.4 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Requires-Dist: wagtail (>=1.0) UNKNOWN PKZG*)AZZ$wagtail_polls-1.0.2.dist-info/RECORDpolls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 polls/admin.py,sha256=Xff_wfl3M5PLq-fDfBlAwVVBzmzbYLT6x86lJ0V8rX0,221 polls/models.py,sha256=RlMqNDdegOhMlZRmeMs-DNtTj3sOmk0vwyrsfmzOvzE,1878 polls/serializers.py,sha256=lE1tCpK12_vubGvJPzXc-mJKCoWVvD7Hg6fLtzWFM3s,705 polls/tests.py,sha256=sc-2Px1mb9x3GY_MLrsBCuPSQh5sbuiH5HNKroGREtE,1571 polls/urls.py,sha256=s6Z5nRkfIpcTA-m11fiYq8AxncFUaP0HFqDcIkPO_CM,338 polls/views.py,sha256=BKLZfsFIl9D1ybNqvH1DX6DRxDtvJpxVh-7rFALD2Mk,1380 polls/migrations/0001_initial.py,sha256=kJjLTuCvy-RNGo8oUBWpw5ovOQlR2MBmDS0gtBgMPNQ,2282 polls/migrations/0002_auto_20151005_1920.py,sha256=D-QAqe9TuKQ_R6SB-ZKr61aSU6ExRfDNFrUObEsXSTQ,1467 polls/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 wagtail_polls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 wagtail_polls/settings.py,sha256=RmZbrKDw-wB5LwOtGnNeFUBYD21wxHry4jAg8W1k0Ag,3107 wagtail_polls/urls.py,sha256=qf7xcMKVa3iVWZNAhUh17_AW9D8DKiqrXsLP9CInflk,355 wagtail_polls/wsgi.py,sha256=9e-eRCKHehChC6DQrJaRLOGzZ9rEN-uDgMNTkUKHNiY,403 wagtail_polls-1.0.2.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 wagtail_polls-1.0.2.dist-info/METADATA,sha256=GwQ47Id8XC2iRaj3VJPzovLiPz0TxBg0yvnO8mkgY6w,635 wagtail_polls-1.0.2.dist-info/RECORD,, wagtail_polls-1.0.2.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 wagtail_polls-1.0.2.dist-info/metadata.json,sha256=xHJy8doeNX9jlpD9SkG9RUfbr9rWlS_NpW9BAhyFoQQ,814 wagtail_polls-1.0.2.dist-info/top_level.txt,sha256=i1SlY4NB4WCzwrSHL7Z6_ULxnEzGcYoyFn7ibzmrv8k,20 PKEG`# # wagtail_polls/settings.pyPKEGd"ccZ wagtail_polls/urls.pyPKREG}t wagtail_polls/wsgi.pyPKREGwagtail_polls/__init__.pyPK)IG?riVVpolls/models.pyPKZGB0ddppolls/views.pyPKbEGc##polls/tests.pyPKLEG,RR O#polls/urls.pyPKEG |$polls/serializers.pyPKѣG'polls/__init__.pyPK0EGN5'polls/admin.pyPKEG3"+(polls/migrations/0002_auto_20151005_1920.pyPKѣGq!<. .polls/migrations/0001_initial.pyPKѣG#8polls/migrations/__init__.pyPKZG^- -]8wagtail_polls-1.0.2.dist-info/DESCRIPTION.rstPKZGk..+8wagtail_polls-1.0.2.dist-info/metadata.jsonPKZGr+)<wagtail_polls-1.0.2.dist-info/top_level.txtPKZG}\\#<wagtail_polls-1.0.2.dist-info/WHEELPKZGn3{{&#=wagtail_polls-1.0.2.dist-info/METADATAPKZG*)AZZ$?wagtail_polls-1.0.2.dist-info/RECORDPK~F