Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ben Homework #5

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cc3b371
Added pycharm file to gitignore
benred42 Jun 18, 2015
ed9c644
Created new Django project urly_bird
benred42 Jun 18, 2015
e326db4
Altered settings to use pyscopg2 instead of sqlite3
benred42 Jun 18, 2015
618000a
Started new app accounts to handle user registration/login
benred42 Jun 18, 2015
c422006
Set up login page
benred42 Jun 18, 2015
82e07d6
Added static files for bootstrap
benred42 Jun 18, 2015
2d858d2
Added user registration page
benred42 Jun 18, 2015
e15ff4d
Added new app links for handling all the link bookmarking and display
benred42 Jun 18, 2015
de96a53
Added hashid to requirements.txt
benred42 Jun 18, 2015
6abaed9
Added bookmark and click models to links app
benred42 Jun 18, 2015
0f33b6d
Added the correct hashid library to requirements.txt
benred42 Jun 19, 2015
82027b0
LARGE COMMIT: Added ability to add/edit/delete bookmarks
benred42 Jun 19, 2015
dcd4df0
Changed create/edit/delete pages to redirect you to the page you were…
benred42 Jun 19, 2015
288e3b2
Added url encoding to GET parameters
benred42 Jun 19, 2015
8da8f41
Added background image and some styling
benred42 Jun 19, 2015
e9737ae
Added matplotlib, pandas, and numpy to requirements.txt
benred42 Jun 20, 2015
60f5795
Added a page for each bookmark that displays the clicks per day for t…
benred42 Jun 20, 2015
f90cfdd
Added stats page specific to user
benred42 Jun 20, 2015
d41df6a
Added loginrequiredmixin to user stats view
benred42 Jun 20, 2015
5f5427b
Changed some styling and added bookmark redirect links to user stats …
benred42 Jun 20, 2015
e83ecb4
Added search functions for users and bookmarks, as well as allowing a…
benred42 Jun 22, 2015
4423048
added api app and hooked up serializer for bookmarks
benred42 Jun 23, 2015
d226ba7
Added django rest framework to requirements.txt
benred42 Jun 23, 2015
36c01db
Refactored field name 'url' under Bookmark model to be 'URL'
benred42 Jun 23, 2015
6d3dc36
Added click serialization and display of number of clicks per bookmar…
benred42 Jun 23, 2015
93d70d3
Added an api resource that displays information on the logged in user…
benred42 Jun 24, 2015
a9bc997
Added django-filters to requirements.txt
benred42 Jun 24, 2015
7045126
Added django-filters to setting.py and changed some names in urls.py
benred42 Jun 24, 2015
c8ae41d
Changed permissions such that only anonymous users can create new use…
benred42 Jun 24, 2015
2f650ca
Fixed auto-now bug that prevented graphs from showing (although times…
benred42 Jun 24, 2015
e082b45
Refixed timestamp issue so that charts display quickly and timestamps…
benred42 Jun 25, 2015
bfd84e7
Fixed timestamp issue for clicks as well
benred42 Jun 25, 2015
7b995b9
Made project heroku deployable
benred42 Jun 25, 2015
c5bfced
Minor fixes plus adjustments for heroku
benred42 Jul 27, 2015
b63aa25
More heroku changes
benred42 Jul 27, 2015
71d2d29
More changes for heroku
benred42 Jul 27, 2015
e7c5e12
Made migrations
benred42 Aug 1, 2015
0d59739
Fixed create click for anonymous users
benred42 Aug 1, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ docs/_build/
# PyBuilder
target/

# PyCharm
.idea/
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn urly_bird.wsgi --log-file -
19 changes: 19 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
dj-database-url==0.3.0
dj-static==0.0.6
Django==1.8.2
django-bootstrap3==5.4.0
django-debug-toolbar==1.3.0
django-extensions==1.5.5
django-filter==0.10.0
django-toolbelt==0.0.1
djangorestframework==3.1.3
fake-factory==0.5.1
gnureadline==6.3.3
gunicorn==19.3.0
hashID==3.1.4
hashids==1.1.0
ipython==3.1.0
matplotlib==1.4.3
nose==1.3.7
numpy==1.9.2
pandas==0.16.2
psycopg2==2.6.1
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2015.4
six==1.9.0
sqlparse==0.1.15
static3==0.6.1
Werkzeug==0.10.4
1 change: 1 addition & 0 deletions runtime.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python-3.4.3
Empty file added urly_bird/accounts/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions urly_bird/accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
10 changes: 10 additions & 0 deletions urly_bird/accounts/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django import forms
from django.contrib.auth.models import User


class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())

class Meta:
model = User
fields = ('username', 'email', 'password',)
Empty file.
3 changes: 3 additions & 0 deletions urly_bird/accounts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
31 changes: 31 additions & 0 deletions urly_bird/accounts/templates/registration/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% load bootstrap3 %}

{% block content %}

{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<div class="container">
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<div class="form-group form-custom-narrow">
{% bootstrap_form form %}
</div>
<input type="submit" class="btn btn-default" value="login"/>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
</div>
{# Assumes you setup the password_reset view in your URLconf #}
{#<p><a href="{% url 'password_reset' %}">Lost password?</a></p>#}

{% endblock %}
18 changes: 18 additions & 0 deletions urly_bird/accounts/templates/registration/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load bootstrap3 %}
{% block content %}
<h2>Register</h2>

<div class="container">
<form method="POST" action="{% url 'user_register' %}">

{% csrf_token %}
<div class="form-group form-custom-narrow">
{% bootstrap_form user_form %}
</div>
<input type="submit" class="btn btn-default" value="Register"/>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
</div>

{% endblock %}
3 changes: 3 additions & 0 deletions urly_bird/accounts/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
30 changes: 30 additions & 0 deletions urly_bird/accounts/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib import messages
from accounts.forms import UserForm

# Create your views here.
def register_rater(request):
if request.method == "POST":
user_form = UserForm(request.POST)
if user_form.is_valid():
user = user_form.save()

password = user.password
user.set_password(password)
user.save()

user = authenticate(username=user.username,
password=password)

login(request, user)

messages.add_message(
request,
messages.SUCCESS,
"Welcome, {}. You have successfully created an account and are now logged in".format(user.username))

return redirect("all_bookmarks")
else:
user_form = UserForm()
return render(request, "registration/register.html", {'user_form': user_form})
Empty file added urly_bird/api/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions urly_bird/api/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
Empty file.
3 changes: 3 additions & 0 deletions urly_bird/api/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
20 changes: 20 additions & 0 deletions urly_bird/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
else:
return request.user == obj.user


class IsSameUser(permissions.BasePermission):
def has_permission(self, request, view):
if request.method == "POST":
return request.user.is_anonymous()
else:
return True

def has_object_permission(self, request, view, obj):
return request.user == obj
96 changes: 96 additions & 0 deletions urly_bird/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from django.db.models import Count
from django.utils import timezone
from links.models import Bookmark, Click
from rest_framework import serializers
from hashids import Hashids
from rest_framework.reverse import reverse


class ClickSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
bookmark = serializers.PrimaryKeyRelatedField(read_only=True)
timestamp = serializers.SerializerMethodField()

class Meta:
model = Click

def create(self, validated_data):
"""Automatically sets the timestamp for a bookmark."""
click = Click.objects.create(**validated_data)
click.timestamp = timezone.now()
click.save()
return click


def get_timestamp(self, obj):
return obj.timestamp


#######################################################################################################################

class BookmarkSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
api_url = serializers.HyperlinkedIdentityField(view_name='bookmark-detail')
code = serializers.SerializerMethodField()
timestamp = serializers.SerializerMethodField()
click_number = serializers.IntegerField(source='num_clicks', read_only=True)
click_count = serializers.SerializerMethodField()
_links = serializers.SerializerMethodField()

class Meta:
model = Bookmark
fields = (
'id', 'api_url', 'code', 'url', 'title', 'description', 'timestamp', 'user', 'click_number', 'click_count',
'_links')

def get_code(self, obj):
return obj.code

def get_timestamp(self, obj):
return obj.timestamp

def get__links(self, obj):
links = {
"clicks": reverse('create_click', kwargs=dict(bookmark_id=obj.id),
request=self.context.get('request'))}
return links

def get_click_count(self, obj):
bookmark = Bookmark.objects.filter(pk=obj.id).annotate(click_num=Count('click'))
return bookmark[0].click_num

def create(self, validated_data):
"""Automatically sets the short url code and the timestamp for a bookmark."""
bookmark = Bookmark.objects.create(**validated_data)
hashids = Hashids(salt="Hopefully the URLyBird does not get any worms")
code = hashids.encode(bookmark.id)
bookmark.code = code
bookmark.timestamp = timezone.now()
bookmark.save()
return bookmark


#######################################################################################################################

class UserSerializer(serializers.HyperlinkedModelSerializer):
total_clicks = serializers.IntegerField(source='num_clicks', read_only=True)
bookmark_set = BookmarkSerializer(many=True, read_only=True)

class Meta:
model = User
fields = ('id', 'url', 'username', 'email', 'total_clicks', 'bookmark_set', 'password')
write_only_fields = ('password',)

def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()

user = authenticate(username=user.username,
password=validated_data['password'])

login(self.context.get('request'), user)

return user
3 changes: 3 additions & 0 deletions urly_bird/api/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
58 changes: 58 additions & 0 deletions urly_bird/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.contrib.auth.models import User
from django.db.models import Count
from rest_framework import viewsets, permissions, generics, filters
from api.serializers import BookmarkSerializer, ClickSerializer, UserSerializer
from links.models import Bookmark, Click
from api.permissions import IsOwnerOrReadOnly, IsSameUser
import django_filters


class BookmarkFilter(django_filters.FilterSet):
title = django_filters.CharFilter(name="title", lookup_type="icontains")
description = django_filters.CharFilter(name="description", lookup_type="icontains")
user = django_filters.NumberFilter(name='user', lookup_type="exact")

class Meta:
model = Bookmark
fields = ['title', 'description', 'user']


class BookmarkViewSet(viewsets.ModelViewSet):
"""Add the following search terms to the url to filter results:
title=your+title+search
description=your+description+search
user=your+user+id+search
Example: http://host/api/bookmarks/?title=nest&description=birds+live"""
queryset = Bookmark.objects.all().annotate(num_clicks=Count('click'))
serializer_class = BookmarkSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly)
filter_backends = (filters.DjangoFilterBackend,)
filter_class = BookmarkFilter

def perform_create(self, serializer):
serializer.save(user=self.request.user)


class ClickView(generics.ListCreateAPIView):
serializer_class = ClickSerializer

def get_queryset(self):
return Click.objects.filter(bookmark__id=self.kwargs['bookmark_id'])

def perform_create(self, serializer):
bookmark = Bookmark.objects.filter(pk=self.kwargs['bookmark_id'])[0]
if self.request.user.is_authenticated():
serializer.save(user=self.request.user, bookmark=bookmark)
else:
serializer.save(user=User.objects.get(pk=103), bookmark=bookmark)


class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
permission_classes = (IsSameUser,)

def get_queryset(self):
queryset = User.objects.all().annotate(num_clicks=Count('bookmark__click')).filter(
pk=self.request.user.id)
return queryset
Empty file added urly_bird/links/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions urly_bird/links/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.contrib import admin
from links.models import Bookmark, Click

class BookmarkAdmin(admin.ModelAdmin):
list_display = ["id", "title", "description", "url", "code", "timestamp", "user"]

class ClickAdmin(admin.ModelAdmin):
list_display = ["timestamp", "bookmark"]

# Register your models here.
admin.site.register(Bookmark, BookmarkAdmin)
admin.site.register(Click, ClickAdmin)
20 changes: 20 additions & 0 deletions urly_bird/links/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django import forms
from links.models import Bookmark
from django.utils import timezone



class BookmarkForm(forms.ModelForm):
url = forms.URLField()
title = forms.CharField()
description = forms.CharField(widget=forms.Textarea, required=False)
code = forms.CharField(widget=forms.HiddenInput(), required=False)
timestamp = forms.DateTimeField(widget=forms.HiddenInput(), initial=timezone.now)

class Meta:
model = Bookmark
fields = ("url", "title", "description", "code", "timestamp")


class EditBookmarkForm(BookmarkForm):
code = forms.CharField()
Loading