Verified Commit a8469013 authored by David Haynes's avatar David Haynes 🙆
Browse files

Django 2.0 + Python 3 adoption

- style guide: 2018 edition
- thankfully nothing big breaks
- man this is a bigger project than I remember
parent 76d358c7
Pipeline #2111 failed with stages
in 1 minute and 24 seconds
""" """
go/admin.py go/admin.py
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
Configure the Django admin pages and apply optional formatting.
"""
# Django Imports # Django Imports
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
...@@ -18,7 +15,6 @@ class URLAdmin(admin.ModelAdmin): ...@@ -18,7 +15,6 @@ class URLAdmin(admin.ModelAdmin):
""" """
Define what attributes display in the URL Admin Define what attributes display in the URL Admin
""" """
list_display = ("target", "short", "owner", "clicks", "date_created", "expires") list_display = ("target", "short", "owner", "clicks", "date_created", "expires")
# Register URLAdmin # Register URLAdmin
...@@ -28,7 +24,6 @@ class RegisteredUserInline(admin.StackedInline): ...@@ -28,7 +24,6 @@ class RegisteredUserInline(admin.StackedInline):
""" """
Define an inline admin descriptor for User model Define an inline admin descriptor for User model
""" """
model = RegisteredUser model = RegisteredUser
can_delete = False can_delete = False
...@@ -36,10 +31,7 @@ class UserAdmin(UserAdmin): ...@@ -36,10 +31,7 @@ class UserAdmin(UserAdmin):
""" """
Define a new User admin Define a new User admin
""" """
# see above class that we defined
inlines = (RegisteredUserInline, ) inlines = (RegisteredUserInline, )
# and modify User to use our new UserAdmin
admin.site.unregister(User) admin.site.unregister(User)
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
""" """
go/cas_callbacks.py go/cas_callbacks.py
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
Parse the CAS/PF responses and create users in the database.
"""
# Django Imports # Django Imports
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -17,7 +14,6 @@ def pfparse(pf_name_result): ...@@ -17,7 +14,6 @@ def pfparse(pf_name_result):
""" """
Parse what peoplefinder sends back to us and make a list out of it Parse what peoplefinder sends back to us and make a list out of it
""" """
# name comes in format of Anderson, Nicholas J # name comes in format of Anderson, Nicholas J
name_list = pf_name_result.split(',') name_list = pf_name_result.split(',')
# there's random whitespace with the first name # there's random whitespace with the first name
...@@ -37,7 +33,6 @@ def pfinfo(uname): ...@@ -37,7 +33,6 @@ def pfinfo(uname):
""" """
Get information from peoplefinder Get information from peoplefinder
""" """
base_url = settings.PF_URL base_url = settings.PF_URL
url = base_url + "basic/all/" + str(uname) url = base_url + "basic/all/" + str(uname)
try: try:
...@@ -80,9 +75,8 @@ def pfinfo(uname): ...@@ -80,9 +75,8 @@ def pfinfo(uname):
def create_user(tree): def create_user(tree):
""" """
Create a django user based off of the peoplefinder info we parsed earlier Create a django user based off of the peoplefinder info we parsed earlier.
""" """
print("Parsing CAS information.") print("Parsing CAS information.")
try: try:
username = tree[0][0].text username = tree[0][0].text
...@@ -112,10 +106,8 @@ def create_user(tree): ...@@ -112,10 +106,8 @@ def create_user(tree):
print("Added user's name, %s %s." % (info_name[0], info_name[1])) print("Added user's name, %s %s." % (info_name[0], info_name[1]))
print("User object creation process completed.") print("User object creation process completed.")
else: else:
print("User object already exists.") print("User object already exists.")
print("CAS callback successful.") print("CAS callback successful.")
except Exception as ex: except Exception as ex:
print("Unhandled user creation error:", ex) print("Unhandled user creation error:", ex)
""" """
go/forms.py go/forms.py
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
Configure the layout and styling of the Go's forms.
"""
# Python stdlib Imports # Python stdlib Imports
from datetime import datetime, timedelta from datetime import datetime, timedelta
...@@ -30,29 +28,10 @@ from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout ...@@ -30,29 +28,10 @@ from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout
class URLForm(ModelForm): class URLForm(ModelForm):
""" """
The form that is used in URL creation. The form that is used in URL creation.
"""
# target -------------------------------------------------------------------
def clean_target(self):
"""
Prevent redirect loop links
"""
# get the entered target link
target = self.cleaned_data.get('target')
# Commented out as this check cannont properly be tested since we cannot
# dynamically generate request.META.get('HTTP_HOST')
# # if the host (go.gmu.edu) is in the entered target link or where it Define custom fields and then render them onto the template.
# # redirects """
# if self.host in final_url or self.host in target: # target ------------------------------------------------------------------
# raise ValidationError("You can't make a Go link to Go silly!")
# else:
# return target
return target
# Custom target URL field
target = URLField( target = URLField(
required=True, required=True,
label='Long URL (Required)', label='Long URL (Required)',
...@@ -62,23 +41,21 @@ class URLForm(ModelForm): ...@@ -62,23 +41,21 @@ class URLForm(ModelForm):
}) })
) )
# short -------------------------------------------------------------------- # short -------------------------------------------------------------------
def unique_short(value): def unique_short(value):
""" """
Check to make sure the short url has not been used Check to make sure the short url has not been used
""" """
try: try:
# if we're able to get a URL with the same short url # if we're able to get a URL with the same short url
URL.objects.get(short__iexact=value) URL.objects.get(short__iexact=value)
except URL.DoesNotExist as ex: except URL.DoesNotExist as ex:
print(ex)
return return
# then raise a ValidationError # then raise a ValidationError
raise ValidationError('Short url already exists.') raise ValidationError('Short url already exists.')
# Custom short-url field with validators.
short = SlugField( short = SlugField(
required=False, required=False,
label='Short URL (Optional)', label='Short URL (Optional)',
...@@ -88,9 +65,7 @@ class URLForm(ModelForm): ...@@ -88,9 +65,7 @@ class URLForm(ModelForm):
min_length=3, min_length=3,
) )
# expires ------------------------------------------------------------------ # expires -----------------------------------------------------------------
# Define some string date standards
DAY = '1 Day' DAY = '1 Day'
WEEK = '1 Week' WEEK = '1 Week'
MONTH = '1 Month' MONTH = '1 Month'
...@@ -106,7 +81,6 @@ class URLForm(ModelForm): ...@@ -106,7 +81,6 @@ class URLForm(ModelForm):
(CUSTOM, CUSTOM), (CUSTOM, CUSTOM),
) )
# Add preset expiration choices.
expires = ChoiceField( expires = ChoiceField(
required=True, required=True,
label='Expiration (Required)', label='Expiration (Required)',
...@@ -119,7 +93,6 @@ class URLForm(ModelForm): ...@@ -119,7 +93,6 @@ class URLForm(ModelForm):
""" """
Check if the selected date is a valid date Check if the selected date is a valid date
""" """
# a valid date is one that is greater than today # a valid date is one that is greater than today
if value > timezone.now(): if value > timezone.now():
return return
...@@ -127,30 +100,18 @@ class URLForm(ModelForm): ...@@ -127,30 +100,18 @@ class URLForm(ModelForm):
else: else:
raise ValidationError('Date must be after today.') raise ValidationError('Date must be after today.')
# Add a custom expiration choice.
expires_custom = DateTimeField( expires_custom = DateTimeField(
required=False, required=False,
label='Custom Date', label='Custom Date',
input_formats=['%m-%d-%Y'], input_formats=['%m-%d-%Y'],
validators=[valid_date], validators=[valid_date],
initial=lambda: datetime.now() + timedelta(days=1), initial=lambda: datetime.now() + timedelta(days=1)
widget=DateTimePicker(
options={
"format": "MM-DD-YYYY",
"pickTime": False,
},
icon_attrs={
"class": "fa fa-calendar",
},
)
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
On initialization of the form, crispy forms renders this layout On initialization of the form, crispy forms renders this layout.
""" """
# Grab that host info # Grab that host info
self.host = kwargs.pop('host', None) self.host = kwargs.pop('host', None)
super(URLForm, self).__init__(*args, **kwargs) super(URLForm, self).__init__(*args, **kwargs)
...@@ -158,7 +119,7 @@ class URLForm(ModelForm): ...@@ -158,7 +119,7 @@ class URLForm(ModelForm):
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_method = 'POST' self.helper.form_method = 'POST'
# Some xtra vars for form css purposes # Some extra vars for form css purposes
self.helper.form_class = 'form-horizontal' self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-1' self.helper.label_class = 'col-md-1'
self.helper.field_class = 'col-md-6' self.helper.field_class = 'col-md-6'
...@@ -198,7 +159,7 @@ class URLForm(ModelForm): ...@@ -198,7 +159,7 @@ class URLForm(ModelForm):
<h4>Set when you would like your Go address to expire:</h4> <h4>Set when you would like your Go address to expire:</h4>
<br />"""), <br />"""),
'expires', 'expires',
Field('expires_custom', template="crispy/customDateField.html"), Field('expires_custom'),
style="background: rgb(#F6F6F6);"), style="background: rgb(#F6F6F6);"),
active=True, active=True,
template='crispy/accordian-group.html'), template='crispy/accordian-group.html'),
...@@ -214,19 +175,22 @@ class URLForm(ModelForm): ...@@ -214,19 +175,22 @@ class URLForm(ModelForm):
""" """
Metadata about this ModelForm Metadata about this ModelForm
""" """
# what model this form is for # what model this form is for
model = URL model = URL
# what attributes are included # what attributes are included
fields = ['target'] fields = ['target']
class EditForm(URLForm): class EditForm(URLForm):
"""
The form that is used in editing URLs.
A modification of the URL creation form... now for editing URLs. Inherit
custom form fields for DRY purposes.
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
On initialization of the form, crispy forms renders this layout On initialization of the form, crispy forms renders this layout.
""" """
# Grab that host info # Grab that host info
self.host = kwargs.pop('host', None) self.host = kwargs.pop('host', None)
super(URLForm, self).__init__(*args, **kwargs) super(URLForm, self).__init__(*args, **kwargs)
...@@ -285,8 +249,11 @@ class EditForm(URLForm): ...@@ -285,8 +249,11 @@ class EditForm(URLForm):
HTML(""" HTML("""
<br />"""), <br />"""),
StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit'))) StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit')))
class Meta(URLForm.Meta): class Meta(URLForm.Meta):
"""
Metadata about this ModelForm
"""
# what attributes are included # what attributes are included
fields = URLForm.Meta.fields fields = URLForm.Meta.fields
...@@ -294,8 +261,6 @@ class SignupForm(ModelForm): ...@@ -294,8 +261,6 @@ class SignupForm(ModelForm):
""" """
The form that is used when a user is signing up to be a RegisteredUser The form that is used when a user is signing up to be a RegisteredUser
""" """
# The full name of the RegisteredUser
full_name = CharField( full_name = CharField(
required=True, required=True,
label='Full Name (Required)', label='Full Name (Required)',
...@@ -304,7 +269,6 @@ class SignupForm(ModelForm): ...@@ -304,7 +269,6 @@ class SignupForm(ModelForm):
help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.", help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.",
) )
# The RegisteredUser's chosen organization
organization = CharField( organization = CharField(
required=True, required=True,
label='Organization (Required)', label='Organization (Required)',
...@@ -313,7 +277,6 @@ class SignupForm(ModelForm): ...@@ -313,7 +277,6 @@ class SignupForm(ModelForm):
help_text="Or whatever \"group\" you would associate with on campus.", help_text="Or whatever \"group\" you would associate with on campus.",
) )
# The RegisteredUser's reason for signing up to us Go
description = CharField( description = CharField(
required=False, required=False,
label='Description (Optional)', label='Description (Optional)',
...@@ -328,16 +291,15 @@ class SignupForm(ModelForm): ...@@ -328,16 +291,15 @@ class SignupForm(ModelForm):
# ***Need to replace lower url with production URL*** # ***Need to replace lower url with production URL***
# ie. go.gmu.edu/about#terms # ie. go.gmu.edu/about#terms
label=mark_safe( label=mark_safe(
'Do you accept the <a href="http://127.0.0.1:8000/about#terms">Terms of Service</a>?' 'Do you accept the <a href="about">Terms of Service</a>?'
), ),
help_text="Esssentially the GMU Responsible Use of Computing policies.", help_text="Esssentially the GMU Responsible Use of Computing policies.",
) )
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
""" """
On initialization of the form, crispy forms renders this layout On initialization of the form, crispy forms renders this layout.
""" """
# Necessary to call request in forms.py, is otherwise restricted to # Necessary to call request in forms.py, is otherwise restricted to
# views.py and models.py # views.py and models.py
self.request = request self.request = request
...@@ -366,7 +328,6 @@ class SignupForm(ModelForm): ...@@ -366,7 +328,6 @@ class SignupForm(ModelForm):
""" """
Metadata about this ModelForm Metadata about this ModelForm
""" """
# what model this form is for # what model this form is for
model = RegisteredUser model = RegisteredUser
# what attributes are included # what attributes are included
......
...@@ -3,10 +3,6 @@ go/commands/expirelinks.py ...@@ -3,10 +3,6 @@ go/commands/expirelinks.py
Remove expired links from the database. Remove expired links from the database.
""" """
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
# Django Imports # Django Imports
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils import timezone from django.utils import timezone
......
...@@ -3,10 +3,6 @@ go/commands/test_expirelinks.py ...@@ -3,10 +3,6 @@ go/commands/test_expirelinks.py
Test that the function to expire Go links actually works. Test that the function to expire Go links actually works.
""" """
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
# Python stdlib Imports # Python stdlib Imports
from datetime import timedelta from datetime import timedelta
......
""" """
go/models.py go/models.py
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
The core of Go: define the business logic through classes that represent
tables containing structured data in the database.
"""
# Python stdlib Imports # Python stdlib Imports
import string import string
...@@ -16,7 +14,6 @@ from django.db import models ...@@ -16,7 +14,6 @@ from django.db import models
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
# Other Imports # Other Imports
from hashids import Hashids # http://hashids.org/python/ from hashids import Hashids # http://hashids.org/python/
...@@ -26,15 +23,13 @@ HASHIDS = Hashids( ...@@ -26,15 +23,13 @@ HASHIDS = Hashids(
salt="srct.gmu.edu", alphabet=(string.ascii_lowercase + string.digits) salt="srct.gmu.edu", alphabet=(string.ascii_lowercase + string.digits)
) )
@python_2_unicode_compatible
class RegisteredUser(models.Model): class RegisteredUser(models.Model):
""" """
This is simply a wrapper model for the user object which, if an object This is simply a wrapper model for the User model which, if an object
exists, indicates that that user is registered. exists, indicates that that user is registered.
""" """
# Let's associate a User to this RegisteredUser # Let's associate a User to this RegisteredUser
user = models.OneToOneField(User) user = models.OneToOneField(User, on_delete="cascade")
# What is your name? # What is your name?
full_name = models.CharField( full_name = models.CharField(
...@@ -62,35 +57,29 @@ class RegisteredUser(models.Model): ...@@ -62,35 +57,29 @@ class RegisteredUser(models.Model):
def __str__(self): def __str__(self):
""" """
str(RegisteredUser) String representation of this object.
""" """
return '<Registered User: %s - Approval Status: %s>' % ( return '<Registered User: %s - Approval Status: %s>' % (
self.user, self.approved self.user, self.approved
) )
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def handle_regUser_creation(sender, instance, created, **kwargs): def handle_regUser_creation(sender, instance, created, **kwargs):
""" """
When a post_save is called on a User object (and it is newly created), this When a post_save is called on a User object (and it is newly created), this
is called to create an associated RegisteredUser is called to create an associated RegisteredUser.
""" """
if created: if created:
RegisteredUser.objects.create(user=instance) RegisteredUser.objects.create(user=instance)
@python_2_unicode_compatible
class URL(models.Model): class URL(models.Model):
""" """
This model represents a stored URL redirection rule. Each URL has an This model represents a stored URL redirection rule. Each URL has an
owner, target url, short identifier, click counter, and expiration owner, target url, short identifier, click counter, and expiration
date. date.
""" """
# Who is the owner of this Go link # Who is the owner of this Go link
owner = models.ForeignKey(RegisteredUser) owner = models.ForeignKey(RegisteredUser, on_delete="cascade")
# When was this link created? # When was this link created?
date_created = models.DateTimeField(default=timezone.now) date_created = models.DateTimeField(default=timezone.now)
...@@ -111,18 +100,16 @@ class URL(models.Model): ...@@ -111,18 +100,16 @@ class URL(models.Model):
def __str__(self): def __str__(self):
""" """
print(URL) String representation of this object.
""" """
return '<Owner: %s - Target URL: %s>' % (