Browse Source

preliminary commit for upgrading to py3, django2. also added black formatting

py3-upgrade
noah 11 months ago
parent
commit
f3ddd0ebcc
38 changed files with 447 additions and 460 deletions
  1. +1
    -1
      api/admin.py
  2. +1
    -3
      api/apps.py
  3. +31
    -14
      api/migrations/0001_initial.py
  4. +18
    -9
      api/migrations/0002_auto_20161115_0306.py
  5. +5
    -7
      api/migrations/0003_auto_20161118_0423.py
  6. +3
    -5
      api/migrations/0004_auto_20161119_0431.py
  7. +3
    -5
      api/migrations/0005_auto_20161121_0051.py
  8. +3
    -5
      api/migrations/0006_auto_20161121_0104.py
  9. +5
    -7
      api/migrations/0007_auto_20161201_0127.py
  10. +3
    -6
      api/migrations/0008_item_embed.py
  11. +13
    -15
      api/migrations/0009_auto_20161207_0239.py
  12. +3
    -5
      api/migrations/0010_auto_20161224_1901.py
  13. +3
    -6
      api/migrations/0011_auto_20180113_1838.py
  14. +9
    -10
      api/models.py
  15. +2
    -4
      api/permissions.py
  16. +35
    -42
      api/scraper.py
  17. +20
    -8
      api/serializers.py
  18. +5
    -9
      api/urls.py
  19. +40
    -26
      api/views.py
  20. +20
    -21
      customauth/admin.py
  21. +1
    -3
      customauth/apps.py
  22. +2
    -4
      customauth/forms.py
  23. +27
    -13
      customauth/migrations/0001_initial.py
  24. +3
    -6
      customauth/migrations/0002_user_position.py
  25. +10
    -15
      customauth/models.py
  26. +6
    -8
      customauth/views.py
  27. +14
    -7
      fabfile.py
  28. +1
    -3
      player/apps.py
  29. +0
    -2
      player/models.py
  30. +2
    -4
      player/urls.py
  31. +2
    -4
      player/views.py
  32. +11
    -12
      requirements.txt
  33. +1
    -3
      stats/apps.py
  34. +0
    -2
      stats/models.py
  35. +2
    -4
      stats/urls.py
  36. +2
    -4
      stats/views.py
  37. +130
    -137
      watershed/settings.py
  38. +10
    -21
      watershed/urls.py

+ 1
- 1
api/admin.py View File

@ -1,6 +1,6 @@
from django.contrib import admin
# Register your models here.
from models import Item
from .models import Item
admin.site.register(Item)

+ 1
- 3
api/apps.py View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'
name = "api"

+ 31
- 14
api/migrations/0001_initial.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-12 03:45
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
@ -17,21 +15,40 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='Item',
name="Item",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uri', models.URLField()),
('platform', models.IntegerField(choices=[(0, 'Soundcloud'), (1, 'Bandcamp'), (2, 'Youtube')])),
('referrer', models.URLField()),
('position', models.PositiveIntegerField()),
('added_on', models.DateTimeField(auto_now_add=True)),
('played', models.BooleanField(default=False)),
('played_on', models.DateTimeField()),
('fave', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("uri", models.URLField()),
(
"platform",
models.IntegerField(
choices=[(0, "Soundcloud"), (1, "Bandcamp"), (2, "Youtube")]
),
),
("referrer", models.URLField()),
("position", models.PositiveIntegerField()),
("added_on", models.DateTimeField(auto_now_add=True)),
("played", models.BooleanField(default=False)),
("played_on", models.DateTimeField()),
("fave", models.BooleanField(default=False)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
'ordering': ('position',),
"ordering": ("position",),
},
),
]

+ 18
- 9
api/migrations/0002_auto_20161115_0306.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-15 03:06
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
@ -10,18 +8,29 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0001_initial'),
("api", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name='item',
name='platform',
field=models.IntegerField(choices=[(0, 'Unknown'), (1, 'Bandcamp'), (2, 'Youtube'), (3, 'Soundcloud')]),
model_name="item",
name="platform",
field=models.IntegerField(
choices=[
(0, "Unknown"),
(1, "Bandcamp"),
(2, "Youtube"),
(3, "Soundcloud"),
]
),
),
migrations.AlterField(
model_name='item',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to=settings.AUTH_USER_MODEL),
model_name="item",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="items",
to=settings.AUTH_USER_MODEL,
),
),
]

+ 5
- 7
api/migrations/0003_auto_20161118_0423.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-18 04:23
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,18 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0002_auto_20161115_0306'),
("api", "0002_auto_20161115_0306"),
]
operations = [
migrations.AddField(
model_name='item',
name='artist',
model_name="item",
name="artist",
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='item',
name='title',
model_name="item",
name="title",
field=models.CharField(max_length=255, null=True),
),
]

+ 3
- 5
api/migrations/0004_auto_20161119_0431.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-19 04:31
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0003_auto_20161118_0423'),
("api", "0003_auto_20161118_0423"),
]
operations = [
migrations.AlterField(
model_name='item',
name='referrer',
model_name="item",
name="referrer",
field=models.URLField(null=True),
),
]

+ 3
- 5
api/migrations/0005_auto_20161121_0051.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-21 00:51
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_auto_20161119_0431'),
("api", "0004_auto_20161119_0431"),
]
operations = [
migrations.AlterField(
model_name='item',
name='played_on',
model_name="item",
name="played_on",
field=models.DateTimeField(null=True),
),
]

+ 3
- 5
api/migrations/0006_auto_20161121_0104.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-21 01:04
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0005_auto_20161121_0051'),
("api", "0005_auto_20161121_0051"),
]
operations = [
migrations.AlterField(
model_name='item',
name='position',
model_name="item",
name="position",
field=models.PositiveIntegerField(default=1),
),
]

+ 5
- 7
api/migrations/0007_auto_20161201_0127.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-12-01 01:27
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,17 +6,17 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0006_auto_20161121_0104'),
("api", "0006_auto_20161121_0104"),
]
operations = [
migrations.RemoveField(
model_name='item',
name='played',
model_name="item",
name="played",
),
migrations.AlterField(
model_name='item',
name='position',
model_name="item",
name="position",
field=models.IntegerField(default=1),
),
]

+ 3
- 6
api/migrations/0008_item_embed.py View File

@ -1,20 +1,17 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-12-07 00:57
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0007_auto_20161201_0127'),
("api", "0007_auto_20161201_0127"),
]
operations = [
migrations.AddField(
model_name='item',
name='embed',
model_name="item",
name="embed",
field=models.TextField(null=True),
),
]

+ 13
- 15
api/migrations/0009_auto_20161207_0239.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-12-07 02:39
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,32 +6,32 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0008_item_embed'),
("api", "0008_item_embed"),
]
operations = [
migrations.AlterField(
model_name='item',
name='artist',
field=models.CharField(blank=True, default='', max_length=255),
model_name="item",
name="artist",
field=models.CharField(blank=True, default="", max_length=255),
preserve_default=False,
),
migrations.AlterField(
model_name='item',
name='embed',
field=models.TextField(blank=True, default=''),
model_name="item",
name="embed",
field=models.TextField(blank=True, default=""),
preserve_default=False,
),
migrations.AlterField(
model_name='item',
name='referrer',
field=models.URLField(blank=True, default=''),
model_name="item",
name="referrer",
field=models.URLField(blank=True, default=""),
preserve_default=False,
),
migrations.AlterField(
model_name='item',
name='title',
field=models.CharField(blank=True, default='', max_length=255),
model_name="item",
name="title",
field=models.CharField(blank=True, default="", max_length=255),
preserve_default=False,
),
]

+ 3
- 5
api/migrations/0010_auto_20161224_1901.py View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-12-24 19:01
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0009_auto_20161207_0239'),
("api", "0009_auto_20161207_0239"),
]
operations = [
migrations.AlterField(
model_name='item',
name='position',
model_name="item",
name="position",
field=models.IntegerField(null=True),
),
]

+ 3
- 6
api/migrations/0011_auto_20180113_1838.py View File

@ -1,20 +1,17 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2018-01-13 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0010_auto_20161224_1901'),
("api", "0010_auto_20161224_1901"),
]
operations = [
migrations.AlterField(
model_name='item',
name='played_on',
model_name="item",
name="played_on",
field=models.DateTimeField(default=None, null=True),
),
]

+ 9
- 10
api/models.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save
@ -8,10 +6,10 @@ from django.dispatch import receiver
from rest_framework.authtoken.models import Token
PLATFORMS = (
(0, 'Unknown'),
(1, 'Bandcamp'),
(2, 'Youtube'),
(3, 'Soundcloud'),
(0, "Unknown"),
(1, "Bandcamp"),
(2, "Youtube"),
(3, "Soundcloud"),
)
@ -24,8 +22,9 @@ class ItemManager(models.Manager):
class Item(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='items',
on_delete=models.CASCADE)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name="items", on_delete=models.CASCADE
)
uri = models.URLField()
platform = models.IntegerField(choices=PLATFORMS)
artist = models.CharField(max_length=255, blank=True)
@ -44,7 +43,7 @@ class Item(models.Model):
return " - ".join([self.user.__unicode__(), self.artist, self.title])
class Meta:
ordering = ('position',)
ordering = ("position",)
def save(self, *args, **kwargs):
if self.position is None:
@ -55,6 +54,6 @@ class Item(models.Model):
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
''' create auth token on user create '''
""" create auth token on user create """
if created:
Token.objects.create(user=instance)

+ 2
- 4
api/permissions.py View File

@ -1,17 +1,15 @@
from __future__ import unicode_literals
from rest_framework import permissions
class IsOwner(permissions.BasePermission):
message = 'No touchy!'
message = "No touchy!"
def has_object_permission(self, request, view, obj):
return obj.user == request.user
class IsSelf(permissions.BasePermission):
message = 'Nah, not cool'
message = "Nah, not cool"
def has_object_permission(self, request, view, obj):
return obj == request.user

+ 35
- 42
api/scraper.py View File

@ -1,26 +1,25 @@
from __future__ import unicode_literals
import logging
import urlparse
from urllib import parse
import requests
from bs4 import BeautifulSoup
from rest_framework.exceptions import UnsupportedMediaType
from models import PLATFORMS
from .models import PLATFORMS
logger = logging.getLogger(__name__)
class UnsupportedPlatformError(UnsupportedMediaType):
'''
"""
Supported platform not found :(
'''
"""
pass
class Scraper():
'''
class Scraper:
"""
pass a URI and receive either:
- an Error (no platform found or something)
- a dict containing the scraped data
@ -29,12 +28,13 @@ class Scraper():
- embed
- title
- artist (optional)
'''
"""
def __init__(self, uri):
self.page = requests.get(uri)
self.uri = self.page.url # this accounts for redirects
if self.page.status_code == 200:
self.soup = BeautifulSoup(self.page.text, 'html5lib')
self.soup = BeautifulSoup(self.page.text, "html5lib")
else:
self.page.raise_for_status()
@ -43,7 +43,7 @@ class Scraper():
getattr(self, PLATFORMS[self.platform][1].lower())()
def detect_platform(self):
'''
"""
first check if platform name in urlstring.
if not, assume bandcamp, try to verify.
this is going to become untenable as platforms get added, and
@ -51,7 +51,7 @@ class Scraper():
- possibly refactor all this into classes for
each platform that we can loop through to run the check,
and if matched, return the data or smth
'''
"""
for i, v in PLATFORMS[1:]: # index 0 is "Unknown"
if v.lower() in self.uri:
return i
@ -60,11 +60,11 @@ class Scraper():
if "youtu.be" in self.uri:
return 2
twitter = self.soup.find('meta', property="twitter:site")
if twitter and twitter.attrs['content'] in ("bandcamp", "@bandcamp"):
twitter = self.soup.find("meta", property="twitter:site")
if twitter and twitter.attrs["content"] in ("bandcamp", "@bandcamp"):
return 1
else:
logger.error('no platform found for uri: {}'.format(self.uri))
logger.error("no platform found for uri: {}".format(self.uri))
raise UnsupportedPlatformError("No supported platform found :(")
def bandcamp(self):
@ -72,12 +72,11 @@ class Scraper():
artist = self.soup.select("span[itemprop='byArtist'] > a")[0]
self.artist = artist.getText().strip()
title = self.soup.find("h2",
{'class': 'trackTitle', 'itemprop': 'name'})
title = self.soup.find("h2", {"class": "trackTitle", "itemprop": "name"})
self.title = title.getText().strip()
meta = self.soup.find("meta", property='og:video:secure_url')
embed = dict(meta.attrs)['content']
meta = self.soup.find("meta", property="og:video:secure_url")
embed = dict(meta.attrs)["content"]
self.embed = embed.replace("tracklist=false", "tracklist=true")
except IndexError:
# this is the failure case for pages on the bandcamp site
@ -94,38 +93,32 @@ class Scraper():
parts = [i.strip() for i in self.soup.title.getText().split("|")]
raw = parts[0].split(" by ")
self.artist = raw[1].strip(" -")
self.title = raw[0].replace(self.artist, '').strip(" -")
self.title = raw[0].replace(self.artist, "").strip(" -")
data = {
'iframe': True,
'format': 'json',
'auto_play': False,
'url': self.uri
}
r = requests.get('https://soundcloud.com/oembed', data)
data = {"iframe": True, "format": "json", "auto_play": False, "url": self.uri}
r = requests.get("https://soundcloud.com/oembed", data)
response = r.json()
self.embed = response['html']
self.embed = response["html"]
return
def youtube(self):
self.artist = ''
self.title = self.soup.find('title').getText()\
.replace(" - YouTube", "")
query = urlparse.urlparse(self.uri)
qs = urlparse.parse_qs(query[4])
if 'v' in qs.keys():
self.embed = qs['v'][0]
self.artist = ""
self.title = self.soup.find("title").getText().replace(" - YouTube", "")
query = parse(self.uri)
qs = query[3]
if "v" in qs.keys():
self.embed = qs["v"][0]
logger.debug("youtube - v = {}".format(self.embed))
elif 'playlist' in self.uri and 'list' in qs.keys():
elif "playlist" in self.uri and "list" in qs.keys():
# this is not enough to make lists work right; see trello for info
self.embed = qs['list'][0]
self.embed = qs["list"][0]
return
def result(self):
return {
'artist': self.artist,
'embed': self.embed,
'title': self.title,
'platform': self.platform
"artist": self.artist,
"embed": self.embed,
"title": self.title,
"platform": self.platform,
}

+ 20
- 8
api/serializers.py View File

@ -1,12 +1,10 @@
from __future__ import unicode_literals
import logging
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
from models import Item
from .models import Item
User = get_user_model()
@ -16,16 +14,30 @@ logger = logging.getLogger(__name__)
class PositionSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'position',)
fields = (
"id",
"position",
)
class ItemSerializer(BulkSerializerMixin, serializers.ModelSerializer):
'''
"""
todo: extra validation -- only supported platforms
(this should probably be frontend's job, but, just to be sure)
'''
"""
class Meta:
model = Item
fields = ('id', 'user', 'position', 'uri', 'artist', 'embed',
'title', 'platform', 'referrer', 'played_on')
fields = (
"id",
"user",
"position",
"uri",
"artist",
"embed",
"title",
"platform",
"referrer",
"played_on",
)
list_serializer_class = BulkListSerializer

+ 5
- 9
api/urls.py View File

@ -1,18 +1,14 @@
from __future__ import unicode_literals
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
import views
from .views import ItemDetail, JsLog, Position, Queue
urlpatterns = [
url(r'^position/$', views.Position.as_view()),
url(r'^queue/$', views.Queue.as_view()),
url(r'^item/$',
views.ItemDetail.as_view()),
url(r'^jslog/$',
views.JsLog.as_view()),
url(r"^position/$", Position.as_view()),
url(r"^queue/$", Queue.as_view()),
url(r"^item/$", ItemDetail.as_view()),
url(r"^jslog/$", JsLog.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)

+ 40
- 26
api/views.py View File

@ -1,10 +1,12 @@
from __future__ import unicode_literals
import json
import logging
from django.http import HttpResponse, Http404, HttpResponseBadRequest,\
HttpResponseForbidden
from django.http import (
HttpResponse,
Http404,
HttpResponseBadRequest,
HttpResponseForbidden,
)
from django.contrib.auth import get_user_model
from django.views import View
from django.views.decorators.csrf import csrf_exempt
@ -16,22 +18,26 @@ from rest_framework.response import Response
from rest_framework_bulk.mixins import BulkUpdateModelMixin
from rest_framework.authtoken.models import Token
from models import Item
from permissions import IsOwner, IsSelf
from scraper import Scraper, UnsupportedPlatformError
from serializers import ItemSerializer, PositionSerializer
from .models import Item
from .permissions import IsOwner, IsSelf
from .scraper import Scraper, UnsupportedPlatformError
from .serializers import ItemSerializer, PositionSerializer
User = get_user_model()
logger = logging.getLogger(__name__)
jslogger = logging.getLogger('js')
jslogger = logging.getLogger("js")
class Queue(ListCreateAPIView):
'''
"""
view or add to the queue
'''
permission_classes = (permissions.IsAuthenticated, IsOwner,)
"""
permission_classes = (
permissions.IsAuthenticated,
IsOwner,
)
serializer_class = ItemSerializer
def perform_create(self, serializer):
@ -43,7 +49,7 @@ class Queue(ListCreateAPIView):
# previously here we listened for the UnsupportedPlatformError and
# re-raised it. not sure why. but that will either swallow other Errors
# or require great duplication eventually. ugh.
uri = request.data.get('uri')
uri = request.data.get("uri")
if not uri:
return HttpResponseBadRequest("No valid uri was provided.")
@ -68,10 +74,14 @@ class Queue(ListCreateAPIView):
class ItemDetail(BulkUpdateModelMixin, ListCreateAPIView):
'''
"""
view, update, or remove an Item
'''
permission_classes = (permissions.IsAuthenticated, IsOwner,)
"""
permission_classes = (
permissions.IsAuthenticated,
IsOwner,
)
serializer_class = ItemSerializer
def perform_create(self, serializer):
@ -109,10 +119,14 @@ class ItemDetail(BulkUpdateModelMixin, ListCreateAPIView):
class Position(RetrieveUpdateAPIView):
'''
"""
update user's position in list
'''
permission_classes = (permissions.IsAuthenticated, IsSelf,)
"""
permission_classes = (
permissions.IsAuthenticated,
IsSelf,
)
serializer_class = PositionSerializer
def get_object(self):
@ -121,14 +135,14 @@ class Position(RetrieveUpdateAPIView):
class JsLog(View):
def post(self, request):
component = request.POST.get('component')
func = request.POST.get('func')
err = request.POST.get('err')
auth = request.POST.get('auth')
component = request.POST.get("component")
func = request.POST.get("func")
err = request.POST.get("err")
auth = request.POST.get("auth")
if auth:
auth = json.loads(auth)
token = Token.objects.get(key=auth['token'])
token = Token.objects.get(key=auth["token"])
u_str = token.user.email + ": "
else:
u_str = ""
@ -138,9 +152,9 @@ class JsLog(View):
else:
loc = ""
msg = u_str + loc + request.POST.get('str')
msg = u_str + loc + request.POST.get("str")
if (err == 'true'):
if err == "true":
jslogger.error(msg)
else:
jslogger.debug(msg)


+ 20
- 21
customauth/admin.py View File

@ -1,6 +1,6 @@
'''
"""
mostly adapted from the Django custom user docs
'''
"""
from django import forms
from django.contrib import admin
@ -8,7 +8,7 @@ from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from models import User
from .models import User
from passwords.fields import PasswordField
@ -18,19 +18,20 @@ class UserCreationForm(forms.ModelForm):
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
email = forms.EmailField(label='Email Address')
password1 = PasswordField(label='Password')
password2 = PasswordField(label='Confirm password')
email = forms.EmailField(label="Email Address")
password1 = PasswordField(label="Password")
password2 = PasswordField(label="Confirm password")
class Meta:
model = User
fields = ('email',)
fields = ("email",)
def clean_password2(self):
'''
"""
Check that the two password entries match
& enforce minimum length
'''
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
@ -52,11 +53,12 @@ class UserChangeForm(forms.ModelForm):
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'password', 'is_active', 'is_admin')
fields = ("email", "password", "is_active", "is_admin")
def clean_password(self):
# Regardless of what the user provides, return the initial value.
@ -73,25 +75,22 @@ class UserAdmin(BaseUserAdmin):
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'is_admin')
list_filter = ('is_admin',)
list_display = ("email", "is_admin")
list_filter = ("is_admin",)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Permissions', {'fields': ('is_admin',)}),
(None, {"fields": ("email", "password")}),
("Permissions", {"fields": ("is_admin",)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(
None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')}
),
(None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),
)
search_fields = ('email',)
ordering = ('email',)
search_fields = ("email",)
ordering = ("email",)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(User, UserAdmin)
# ... and, since we're not using Django's built-in permissions,


+ 1
- 3
customauth/apps.py View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class CustomauthConfig(AppConfig):
name = 'customauth'
name = "customauth"

+ 2
- 4
customauth/forms.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django import forms
from django.core.exceptions import ValidationError
@ -13,8 +11,8 @@ class RegistrationForm(forms.Form):
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
password = cleaned_data.get('password')
password2 = cleaned_data.get('password2')
password = cleaned_data.get("password")
password2 = cleaned_data.get("password2")
if password != password2:
raise ValidationError("Passwords must match!")


+ 27
- 13
customauth/migrations/0001_initial.py View File

@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-12 03:47
from __future__ import unicode_literals
from django.db import migrations, models
@ -9,22 +6,39 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='User',
name="User",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('email', models.EmailField(max_length=255, unique=True, verbose_name='email address')),
('is_active', models.BooleanField(default=True)),
('is_admin', models.BooleanField(default=False)),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"email",
models.EmailField(
max_length=255, unique=True, verbose_name="email address"
),
),
("is_active", models.BooleanField(default=True)),
("is_admin", models.BooleanField(default=False)),
],
options={
'abstract': False,
"abstract": False,
},
),
]

+ 3
- 6
customauth/migrations/0002_user_position.py View File

@ -1,20 +1,17 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2018-01-13 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('customauth', '0001_initial'),
("customauth", "0001_initial"),
]
operations = [
migrations.AddField(
model_name='user',
name='position',
model_name="user",
name="position",
field=models.IntegerField(default=0),
),
]

+ 10
- 15
customauth/models.py View File

@ -1,8 +1,6 @@
'''
"""
alt models
'''
from __future__ import unicode_literals
"""
from django.db import models
@ -12,7 +10,7 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('Accounts must have an email address')
raise ValueError("Accounts must have an email address")
user = self.model(email=self.normalize_email(email))
user.set_password(password)
@ -27,16 +25,13 @@ class UserManager(BaseUserManager):
class User(AbstractBaseUser):
'''
"""
override User model
'''
"""
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
blank=False
)
verbose_name="email address", max_length=255, unique=True, blank=False
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
@ -53,13 +48,13 @@ class User(AbstractBaseUser):
def __unicode__(self):
return self.email
USERNAME_FIELD = 'email'
USERNAME_FIELD = "email"
def has_perm(self, perm, obj=None):
'''
"""
TODO: custom perms for reading a user's updates (at the minimum)
(OR - separate Account model?? ugh idk)
'''
"""
if self.is_admin:
return True


+ 6
- 8
customauth/views.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import json
import logging
@ -10,7 +8,7 @@ from django.views.decorators.csrf import csrf_exempt
from rest_framework.authtoken.models import Token
from forms import RegistrationForm
from .forms import RegistrationForm
User = get_user_model()
@ -24,7 +22,7 @@ def register(request):
else:
form = RegistrationForm()
return render(request, 'register.html', {'form': form})
return render(request, "register.html", {"form": form})
@csrf_exempt
@ -32,15 +30,15 @@ def register_rest(request):
if request.method == "POST":
form = RegistrationForm(json.loads(request.body))
if form.is_valid():
username = form.cleaned_data.get('email_address')
password = form.cleaned_data.get('password')
username = form.cleaned_data.get("email_address")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
if user is None:
user = User.objects.create_user(username, password)
login(request, user)
return JsonResponse({'token': user.auth_token.key})
return JsonResponse({"token": user.auth_token.key})
else:
return JsonResponse({'errors': form.errors}, status=400)
return JsonResponse({"errors": form.errors}, status=400)
else:
return HttpResponse(status=405)

+ 14
- 7
fabfile.py View File

@ -8,18 +8,22 @@ import pytz
@task
def test(c):
c.run('ls -al /srv/watershed/deploys', echo=True)
c.run("ls -al /srv/watershed/deploys", echo=True)
@task(hosts=['prod'])
@task(hosts=["prod"])
def deploy(c, migrate=False):
"""
deploy to production.
"""
GIT_SHA = git_hash()
TIMESTAMP = datetime.datetime.now(pytz.timezone("America/New_York"))\
.replace(microsecond=0, tzinfo=None).isoformat().replace(':', '.')
TIMESTAMP = (
datetime.datetime.now(pytz.timezone("America/New_York"))
.replace(microsecond=0, tzinfo=None)
.isoformat()
.replace(":", ".")
)
DEPLOY_DIR = "/srv/watershed/deploys/{}_{}".format(TIMESTAMP, GIT_SHA)
# ensure origin/master stays up to date
@ -31,7 +35,10 @@ def deploy(c, migrate=False):
c.local("sentry-cli releases set-commits --auto {}".format(VERSION), echo=True)
c.local("git push deploy master", echo=True)
c.run("/usr/bin/git clone file:///srv/watershed/watershed.git {}".format(DEPLOY_DIR), echo=True)
c.run(
"/usr/bin/git clone file:///srv/watershed/watershed.git {}".format(DEPLOY_DIR),
echo=True,
)
c.run("virtualenv {}/.venv".format(DEPLOY_DIR), echo=True)
with c.cd(DEPLOY_DIR):
@ -51,12 +58,12 @@ def deploy(c, migrate=False):
c.run("ln -nsf {} /srv/watershed/live".format(DEPLOY_DIR))
c.run(
"ln -nsf /srv/watershed/live/watershed.uwsgi.ini /etc/uwsgi/apps-enabled/",
echo=True
echo=True,
)
c.run("sudo service uwsgi reload")
c.run(
"ln -nsf /srv/watershed/live/watershed.nginx.conf /etc/nginx/sites-enabled/",
echo=True
echo=True,
)
c.run("sudo service nginx reload")


+ 1
- 3
player/apps.py View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class PlayerConfig(AppConfig):
name = 'player'
name = "player"

+ 0
- 2
player/models.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.db import models
# Create your models here.

+ 2
- 4
player/urls.py View File

@ -1,9 +1,7 @@
from __future__ import unicode_literals
from django.conf.urls import url
import views
from .views import index
urlpatterns = [
url('^$', views.index),
url("^$", index),
]

+ 2
- 4
player/views.py View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
from django.shortcuts import render_to_response
from django.shortcuts import render
def index(request):
return render_to_response('player/index.html')
return render("player/index.html")

+ 11
- 12
requirements.txt View File

@ -3,35 +3,34 @@ asn1crypto==0.24.0
bcrypt==3.1.7
beautifulsoup4==4.6.3
certifi==2019.9.11
cffi==1.8.3
cffi==1.14
contextlib2==0.5.5
cryptography==2.7
Django==1.11.29
Django>=2.0.0
django-extensions==1.9.9
django-passwords==0.3.12
django-simple-aes-field==0.1.3
django-webpack-loader==0.6.0
djangorestframework==3.9.1
djangorestframework-bulk==0.2.1
enum34==1.1.6
djangorestframework
djangorestframework-bulk
fabric==2.5.0
html5lib==1.0.1
invoke==1.3.0
ipaddress==1.0.22
oauthlib==2.0.0
oauthlib
paramiko==2.6.0
psycopg2==2.7.5
psycopg2
pycparser==2.17
pycrypto==2.6.1
PyNaCl==1.3.0
pytz==2019.1
raven==6.9.0
requests==2.20.0
requests-oauthlib==0.7.0
sentry-sdk==0.12.2
SimpleAES==1.0
requests
requests-oauthlib
sentry-sdk
SimpleAES
six==1.10.0
South==1.0.2
typing==3.5.2.2
urllib3==1.25.6
urllib3
webencodings==0.5.1

+ 1
- 3
stats/apps.py View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class StatsConfig(AppConfig):
name = 'stats'
name = "stats"

+ 0
- 2
stats/models.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.db import models
# Create your models here.

+ 2
- 4
stats/urls.py View File

@ -1,7 +1,5 @@
from django.conf.urls import url
import views
from .views import index
urlpatterns = [
url(r'^$', views.index)
]
urlpatterns = [url(r"^$", index)]

+ 2
- 4
stats/views.py View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.shortcuts import render
from django.contrib.auth import get_user_model
@ -12,7 +10,7 @@ def index(request):
context = {
"save_count": Item.objects.count(),
"user_count": User.objects.count(),
"play_count": 'coming soon!'
"play_count": "coming soon!",
}
return render(request, 'stats/index.html', context)
return render(request, "stats/index.html", context)

+ 130
- 137
watershed/settings.py View File

@ -1,133 +1,130 @@
# -*- coding: utf-8 -*-
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = os.getenv('WATERSHED_SECRET_KEY')
SECRET_KEY = os.getenv("WATERSHED_SECRET_KEY")
DEBUG = os.getenv('DEBUG', False)
DEBUG = os.getenv("DEBUG", False)
if DEBUG:
ALLOWED_HOSTS = ['watershed.nthall.com', 'watershed-dev.nthall.com']
ALLOWED_HOSTS = ["watershed.nthall.com", "watershed-dev.nthall.com"]
else:
ALLOWED_HOSTS = ['watershed.rocks']
ALLOWED_HOSTS = ["watershed.rocks"]
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# 3rd party
'passwords',
'rest_framework',
'rest_framework.authtoken',
'rest_framework_bulk',
'webpack_loader',
'django_extensions',
'raven.contrib.django.raven_compat',
"passwords",
"rest_framework",
"rest_framework.authtoken",
"rest_framework_bulk",
"webpack_loader",
"django_extensions",
"raven.contrib.django.raven_compat",
# app
'customauth.apps.CustomauthConfig',
'api.apps.ApiConfig',
'player.apps.PlayerConfig',
'stats.apps.StatsConfig'
"customauth.apps.CustomauthConfig",
"api.apps.ApiConfig",
"player.apps.PlayerConfig",
"stats.apps.StatsConfig",
)
AUTH_USER_MODEL = 'customauth.User'
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
AUTH_USER_MODEL = "customauth.User"
MIDDLEWARE = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.auth.middleware.SessionAuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.security.SecurityMiddleware",
)
ROOT_URLCONF = 'watershed.urls'
ROOT_URLCONF = "watershed.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.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = 'watershed.wsgi.application'
WSGI_APPLICATION = "watershed.wsgi.application"
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'watershed',
'USER': 'www-data',
'PASSWORD': os.getenv('WATERSHED_POSTGRESQL_PASSWORD'),
'HOST': '/var/run/postgresql/',