Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
SRCT
go
Commits
45be3a4f
Commit
45be3a4f
authored
Jun 09, 2018
by
David Haynes
🙆
Browse files
Merge branch '176-business-logic' into 'go-three'
Resolve "Move as much business logic into models.py" See merge request
!121
parents
b63d7963
2759aac4
Pipeline
#2519
passed with stage
in 1 minute and 26 seconds
Changes
12
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Pipfile.lock
View file @
45be3a4f
...
...
@@ -32,11 +32,11 @@
},
"django"
:
{
"hashes"
:
[
"sha256:
26b34f4417aa38d895b6b5307177b51bc3f4d53179d8696a5c19dcb50582523c
"
,
"sha256:
71d1a584bb4ad2b4f933d07d02c716755c1394feaac1ce61ce37843ac5401092
"
"sha256:
3eb25c99df1523446ec2dc1b00e25eb2ecbdf42c9d8b0b8b32a204a8db9011f8
"
,
"sha256:
69ff89fa3c3a8337015478a1a0744f52a9fef5d12c1efa01a01f99bcce9bf10c
"
],
"index"
:
"pypi"
,
"version"
:
"==2.0.
5
"
"version"
:
"==2.0.
6
"
},
"django-cas-client"
:
{
"hashes"
:
[
...
...
@@ -127,10 +127,10 @@
"develop"
:
{
"astroid"
:
{
"hashes"
:
[
"sha256:0
32f6e09161e96f417ea7fad46d3fac7a9019c775f202182c22df0e4f714cb1c
"
,
"sha256:
dea42ae6e0b789b543f728ddae7ddb6740ba33a49fb52c4a4d9cb7bb4aa6ec09
"
"sha256:0
ef2bf9f07c3150929b25e8e61b5198c27b0dca195e156f0e4d5bdd89185ca1a
"
,
"sha256:
fc9b582dba0366e63540982c3944a9230cbc6f303641c51483fa547dcc22393a
"
],
"version"
:
"==1.6.
4
"
"version"
:
"==1.6.
5
"
},
"coverage"
:
{
"hashes"
:
[
...
...
@@ -225,11 +225,11 @@
},
"pylint"
:
{
"hashes"
:
[
"sha256:a
a519865f8890a5905fa34924fed0f3bfc7d84fc9f9142c16dac52ffecd25
a3
9
"
,
"sha256:
c353d8225195b37cc3aef18248b8f3fe94c5a6a95affaf885ae21a24ca31d8eb
"
"sha256:a
48070545c12430cfc4e865bf62f5ad367784765681b3db442d8230f0960a
a3
c
"
,
"sha256:
fff220bcb996b4f7e2b0f6812fd81507b72ca4d8c4d05daf2655c333800cb9b3
"
],
"index"
:
"pypi"
,
"version"
:
"==1.9.
1
"
"version"
:
"==1.9.
2
"
},
"pylint-django"
:
{
"hashes"
:
[
...
...
go/go/cas_callbacks.py
View file @
45be3a4f
...
...
@@ -10,9 +10,9 @@ from django.contrib.auth.models import User
# Other Imports
import
requests
def
pfparse
(
pf_name_result
)
:
def
pfparse
(
pf_name_result
:
str
)
->
list
:
"""
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_list
=
pf_name_result
.
split
(
','
)
...
...
@@ -29,12 +29,11 @@ def pfparse(pf_name_result):
new_name_list
=
[
first_name
,
name_list
[
0
]]
return
new_name_list
def
pfinfo
(
uname
)
:
def
pfinfo
(
uname
:
str
)
->
list
:
"""
Get information from peoplefinder
Get information from peoplefinder
.
"""
base_url
=
settings
.
PF_URL
url
=
base_url
+
"basic/all/"
+
str
(
uname
)
url
=
f
"
{
settings
.
PF_URL
}
basic/all/
{
uname
}
"
try
:
metadata
=
requests
.
get
(
url
,
timeout
=
30
)
print
(
"Retrieving information from the peoplefinder api."
)
...
...
@@ -73,7 +72,7 @@ def pfinfo(uname):
print
(
"Returning empty user info tuple."
)
return
[
''
,
''
]
def
create_user
(
tree
):
def
create_user
(
tree
:
list
):
"""
Create a django user based off of the peoplefinder info we parsed earlier.
"""
...
...
go/go/forms.py
View file @
45be3a4f
...
...
@@ -3,34 +3,31 @@ go/forms.py
Configure the layout and styling of the Go's forms.
"""
# Python stdlib Imports
from
datetime
import
datetime
,
timedelta
# Django Imports
from
django.core.exceptions
import
ValidationError
from
django.forms
import
(
BooleanField
,
CharField
,
ChoiceField
,
DateTimeField
,
ModelForm
,
RadioSelect
,
SlugField
,
Textarea
,
TextInput
,
URLField
,
URLInput
)
from
django.utils
import
timezone
ModelForm
,
RadioSelect
,
Textarea
,
TextInput
,
URLField
,
URLInput
)
from
django.utils.safestring
import
mark_safe
from
django.utils
import
timezone
# App Imports
from
.models
import
URL
,
RegisteredUser
# Other Imports
# Third party imports
from
crispy_forms.bootstrap
import
(
Accordion
,
AccordionGroup
,
PrependedText
,
StrictButton
)
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.layout
import
HTML
,
Div
,
Field
,
Fieldset
,
Layout
# App Imports
from
.models
import
URL
,
RegisteredUser
from
.validators
import
regex_short_validator
,
valid_date
class
URLForm
(
ModelForm
):
"""
The form that is used in URL creation.
Define custom fields and then render them onto the template.
"""
# destination -------------------------------------------------------------
-----
# destination -------------------------------------------------------------
destination
=
URLField
(
required
=
True
,
label
=
'Long URL (Required)'
,
...
...
@@ -41,25 +38,11 @@ class URLForm(ModelForm):
)
# short -------------------------------------------------------------------
def
unique_short
(
value
):
"""
Check to make sure the short url has not been used
"""
try
:
# if we're able to get a URL with the same short url
URL
.
objects
.
get
(
short__iexact
=
value
)
except
URL
.
DoesNotExist
as
ex
:
print
(
ex
)
return
# then raise a ValidationError
raise
ValidationError
(
'Short url already exists.'
)
short
=
CharField
(
required
=
False
,
label
=
'Short URL (Optional)'
,
widget
=
TextInput
(),
validators
=
[
unique_short
],
validators
=
[
regex_short_validator
],
max_length
=
20
,
min_length
=
1
,
)
...
...
@@ -88,29 +71,15 @@ class URLForm(ModelForm):
widget
=
RadioSelect
(),
)
def
valid_date
(
value
):
"""
Check if the selected date is a valid date
"""
# a valid date is one that is greater than today
if
value
>
timezone
.
now
():
return
# raise a ValidationError if the date is invalid
else
:
raise
ValidationError
(
'Date must be after today.'
)
expires_custom
=
DateTimeField
(
required
=
False
,
label
=
'Custom Date'
,
input_formats
=
[
'%m-%d-%Y'
],
validators
=
[
valid_date
],
initial
=
lambda
:
datetim
e
.
now
()
+
timedelta
(
days
=
1
)
initial
=
lambda
:
timezon
e
.
now
()
+
timezone
.
timedelta
(
days
=
1
)
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
"""
On initialization of the form, crispy forms renders this layout.
"""
# Grab that host info
self
.
host
=
kwargs
.
pop
(
'host'
,
None
)
super
(
URLForm
,
self
).
__init__
(
*
args
,
**
kwargs
)
...
...
go/go/migrations/0004_auto_20180608_2058.py
0 → 100644
View file @
45be3a4f
# Generated by Django 2.0.5 on 2018-06-08 20:58
from
django.db
import
migrations
,
models
import
django.utils.timezone
import
go.validators
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'go'
,
'0003_auto_20180524_0003'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'date_created'
,
field
=
models
.
DateTimeField
(
default
=
django
.
utils
.
timezone
.
now
,
verbose_name
=
'Go Link Creation Date'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'destination'
,
field
=
models
.
URLField
(
default
=
'https://go.gmu.edu'
,
help_text
=
'The URL to be redirected to when visiting the shortlink.'
,
max_length
=
1000
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'owner'
,
field
=
models
.
ForeignKey
(
on_delete
=
'cascade'
,
to
=
'go.RegisteredUser'
,
verbose_name
=
'RegisteredUser Owner'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'short'
,
field
=
models
.
CharField
(
help_text
=
'The shortcode that acts as the unique go link.'
,
max_length
=
20
,
unique
=
True
,
validators
=
[
go
.
validators
.
unique_short_validator
,
go
.
validators
.
regex_short_validator
]),
),
]
go/go/migrations/0005_auto_20180609_0025.py
0 → 100644
View file @
45be3a4f
# Generated by Django 2.0.5 on 2018-06-09 00:25
import
datetime
from
django.db
import
migrations
,
models
from
django.utils.timezone
import
utc
import
go.validators
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'go'
,
'0004_auto_20180608_2058'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'approved'
,
field
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Approval Status'
),
),
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'blocked'
,
field
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Blocked Status'
),
),
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'description'
,
field
=
models
.
TextField
(
blank
=
True
,
default
=
''
,
verbose_name
=
'Signup Description'
),
),
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'full_name'
,
field
=
models
.
CharField
(
default
=
''
,
max_length
=
100
,
verbose_name
=
'Full Name'
),
),
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'organization'
,
field
=
models
.
CharField
(
default
=
''
,
max_length
=
100
,
verbose_name
=
'Organization'
),
),
migrations
.
AlterField
(
model_name
=
'registereduser'
,
name
=
'registered'
,
field
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Registration Status'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'date_created'
,
field
=
models
.
DateTimeField
(
default
=
datetime
.
datetime
(
2018
,
6
,
9
,
0
,
25
,
38
,
606587
,
tzinfo
=
utc
),
verbose_name
=
'Go Link Creation Date'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'date_expires'
,
field
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
,
verbose_name
=
'Go Link Expiry Date'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'destination'
,
field
=
models
.
URLField
(
default
=
'https://go.gmu.edu'
,
max_length
=
1000
,
verbose_name
=
'Go Link Destination URL'
),
),
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'short'
,
field
=
models
.
CharField
(
max_length
=
20
,
unique
=
True
,
validators
=
[
go
.
validators
.
unique_short_validator
,
go
.
validators
.
regex_short_validator
],
verbose_name
=
'Go Shortcode'
),
),
]
go/go/migrations/0006_auto_20180609_0025.py
0 → 100644
View file @
45be3a4f
# Generated by Django 2.0.5 on 2018-06-09 00:25
import
datetime
from
django.db
import
migrations
,
models
from
django.utils.timezone
import
utc
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'go'
,
'0005_auto_20180609_0025'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'date_created'
,
field
=
models
.
DateTimeField
(
default
=
datetime
.
datetime
(
2018
,
6
,
9
,
0
,
25
,
39
,
319719
,
tzinfo
=
utc
),
verbose_name
=
'Go Link Creation Date'
),
),
]
go/go/migrations/0007_auto_20180609_0026.py
0 → 100644
View file @
45be3a4f
# Generated by Django 2.0.5 on 2018-06-09 00:26
from
django.db
import
migrations
,
models
import
django.utils.timezone
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'go'
,
'0006_auto_20180609_0025'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'url'
,
name
=
'date_created'
,
field
=
models
.
DateTimeField
(
default
=
django
.
utils
.
timezone
.
now
,
verbose_name
=
'Go Link Creation Date'
),
),
]
go/go/models.py
View file @
45be3a4f
...
...
@@ -17,14 +17,12 @@ from django.utils import timezone
# Other Imports
from
hashids
import
Hashids
from
.validators
import
regex_short_validator
,
unique_short_validator
"""
Generate the salt and initialize Hashids
Note: the Hashids library already implements several restrictions on character
placement, including repeating or incrementing numbers, or placing curse word
characters adjacent to one another.
"""
# Generate the salt and initialize Hashids
# Note: the Hashids library already implements several restrictions oncharacter
# placement, including repeating or incrementing numbers, or placing curse word
# characters adjacent to one another.
SIMILAR_CHARS
=
set
([
'b'
,
'G'
,
'6'
,
'g'
,
'q'
,
'l'
,
'1'
,
'I'
,
'S'
,
'5'
,
'O'
,
'0'
])
ALPHANUMERICS
=
set
(
string
.
ascii_letters
+
string
.
digits
)
...
...
@@ -34,7 +32,6 @@ HASHIDS = Hashids(
salt
=
"srct.gmu.edu"
,
alphabet
=
(
LINK_CHARS
)
)
class
RegisteredUser
(
models
.
Model
):
"""
Wrapper model for the built in User model which stores data pertaining to
...
...
@@ -47,49 +44,40 @@ class RegisteredUser(models.Model):
)
full_name
=
models
.
CharField
(
"
verbose n
ame"
,
"
Full N
ame"
,
max_length
=
100
,
default
=
""
,
help_text
=
""
)
organization
=
models
.
CharField
(
"
verbose name
"
,
"
Organization
"
,
max_length
=
100
,
default
=
""
,
help_text
=
""
)
description
=
models
.
TextField
(
"
verbose name
"
,
"
Signup Description
"
,
blank
=
True
,
default
=
""
,
help_text
=
""
)
registered
=
models
.
BooleanField
(
"
verbose name
"
,
"
Registration Status
"
,
default
=
False
,
help_text
=
""
)
approved
=
models
.
BooleanField
(
"
verbose name
"
,
"
Approval Status
"
,
default
=
False
,
help_text
=
""
)
blocked
=
models
.
BooleanField
(
"
verbose name
"
,
"
Blocked Status
"
,
default
=
False
,
help_text
=
""
)
def
__str__
(
self
):
return
"<Registered User: {0} - Approval Status: {1}>"
.
format
(
self
.
user
,
self
.
approved
)
return
f
"<RegisteredUser:
{
self
.
user
}
- Approval Status:
{
self
.
approved
}
>"
@
receiver
(
post_save
,
sender
=
User
)
def
handle_reguser_creation
(
sender
,
instance
,
created
,
**
kwargs
):
...
...
@@ -100,64 +88,40 @@ def handle_reguser_creation(sender, instance, created, **kwargs):
if
created
:
RegisteredUser
.
objects
.
create
(
user
=
instance
)
class
URL
(
models
.
Model
):
"""
The representation of a stored URL redirection rule. Each URL has
attributes that are used for analytic purposes.
"""
# DAY = '1 Day'
# WEEK = '1 Week'
# MONTH = '1 Month'
# CUSTOM = 'Custom Date'
# NEVER = 'Never'
# EXPIRATION_CHOICES = (
# (DAY, DAY),
# (WEEK, WEEK),
# (MONTH, MONTH),
# (NEVER, NEVER),
# (CUSTOM, CUSTOM),
# ) TODO
owner
=
models
.
ForeignKey
(
RegisteredUser
,
on_delete
=
"cascade"
,
verbose_name
=
"
verbose name
"
verbose_name
=
"
RegisteredUser Owner
"
)
date_created
=
models
.
DateTimeField
(
"
verbose nam
e"
,
"
Go Link Creation Dat
e"
,
default
=
timezone
.
now
,
help_text
=
""
)
date_expires
=
models
.
DateTimeField
(
"
verbose nam
e"
,
"
Go Link Expiry Dat
e"
,
blank
=
True
,
null
=
True
,
# choices=EXPIRATION_CHOICES, TODO
# default=NEVER, TODO
help_text
=
""
)
destination
=
models
.
URLField
(
"Go Link Destination URL"
,
max_length
=
1000
,
default
=
"https://go.gmu.edu"
,
help_text
=
""
)
# TODO Validator for Slug + Emoji
"""
# http://stackoverflow.com/a/13752628/6762004
RE_EMOJI = re.compile('[
\U00010000
-
\U0010ffff
]', flags=re.UNICODE)
slug_unicode_re = _lazy_re_compile(r'^[-\w]+\Z')
slug_re = _lazy_re_compile(r'^[-a-zA-Z0-9_]+\Z')
"""
# Note: min_length cannot exist on a model so it is enforced in forms.py
short
=
models
.
CharField
(
"Go Shortcode"
,
max_length
=
20
,
unique
=
True
,
help_text
=
""
validators
=
[
unique_short_validator
,
regex_short_validator
],
)
# TODO Abstract analytics into their own model
...
...
@@ -166,9 +130,7 @@ class URL(models.Model):
socialclicks
=
models
.
IntegerField
(
default
=
0
,
help_text
=
""
)
def
__str__
(
self
):
return
'<Owner: %s - destination URL: %s>'
%
(
self
.
owner
.
user
,
self
.
destination
)
return
f
"<Owner:
{
self
.
owner
.
user
}
- Destination URL:
{
self
.
destination
}
>"
class
Meta
:
ordering
=
[
'short'
]
...
...
go/go/test_forms.py
View file @
45be3a4f
...
...
@@ -74,20 +74,20 @@ class URLFormTest(TestCase):
print
(
form
.
errors
)
self
.
assertFalse
(
form
.
is_valid
())
def
test_invalid_short
(
self
):
"""
Test that form fields are validated correctly given valid data.
"""
form_data
=
{
'destination'
:
'https://srct.gmu.edu'
,
'short'
:
'
test
'
,
'expires'
:
'1 Day'
,
'expires_custom'
:
''
}
form
=
URLForm
(
data
=
form_data
)
print
(
form
.
errors
)
self
.
assertFalse
(
form
.
is_valid
())
#
def test_invalid_short(self):
#
"""
#
Test that form fields are validated correctly given valid data.
#
"""
#
form_data = {
#
'destination': 'https://srct.gmu.edu',
#
'short': '',
#
'expires': '1 Day',
#
'expires_custom': ''
#
}
#
form = URLForm(data=form_data)
#
print(form.errors)
#
self.assertFalse(form.is_valid())
def
test_invalid_expires
(
self
):
"""
...
...
@@ -96,7 +96,7 @@ class URLFormTest(TestCase):
form_data
=
{
'destination'
:
'https://srct.gmu.edu'
,
'short'
:
'pls'
,
'expires'
:
'
None
'
,
'expires'
:
''
,
'expires_custom'
:
''
}
...
...
go/go/test_models.py
View file @
45be3a4f
...
...
@@ -185,7 +185,7 @@ class RegisteredUserTest(TestCase):
"""
get_user
=
User
.
objects
.
get
(
username
=
'dhaynes'
)
get_registered_user
=
RegisteredUser
.
objects
.
get
(
user
=
get_user
)
expected
=
'<Registered
User: dhaynes - Approval Status: False>'
expected
=
'<RegisteredUser: dhaynes - Approval Status: False>'
actual
=
str
(
get_registered_user
)
self
.
assertEqual
(
expected
,
actual
)
...
...
go/go/validators.py
0 → 100644
View file @
45be3a4f
"""
go/validators.py
Reusable validators for objects that are intended to be inserted into the Go
database.
"""
# Python stdlib imports
import
re
# Django imports
from
django.core.exceptions
import
ValidationError
from
django.utils
import
timezone
def
regex_short_validator
(
value
):
"""
Run the short through our regex validation before insertion into the
database.
"""
# http://stackoverflow.com/a/13752628/6762004
re_emoji
=
re
.
compile
(
"^(([
\U00010000
-
\U0010ffff
][
\U0000200D
]?)+)$"
)
re_str
=
re
.
compile
(
"^([-\w]+)$"
)
if
not
re_emoji
.
match
(
value
)
and
not
re_str
.
match
(
value
):
raise
ValidationError
(
"Short url fails regex check."
)
def
valid_date
(
value
):
"""
Check if the selected date is a valid date.
"""
if
value
<
timezone
.
now
():
raise
ValidationError
(
"Date must be after today."
)
def
unique_short_validator
(
value
):
"""
Check to make sure the short url has not been used.
"""
# Circular dependency resolution through a deferred import
from
.models
import
URL
if
URL
.
objects
.
filter
(
short__iexact
=
value
).
count
()
>
0
:
raise
ValidationError
(
"Short url already exists."
)
go/go/views.py
View file @
45be3a4f
...
...
@@ -138,8 +138,6 @@ def my_links(request):
return
index
(
request
)
# Rate limits are completely arbitrary
@
ratelimit
(
key
=
'user'
,
rate
=
'3/m'
,
method
=
'POST'
,
block
=
True
)
@
ratelimit
(
key
=
'user'
,
rate
=
'25/d'
,
method
=
'POST'
,
block
=
True
)
def
post
(
request
,
url_form
):
...
...
@@ -475,7 +473,6 @@ def redirection(request, short):
"""
This view redirects a user based on the short URL they requested.
"""
# Get the current domain info
domain
=
"%s://%s"
%
(
request
.
scheme
,
request
.
META
.
get
(
'HTTP_HOST'
))
+
"/"
...
...
@@ -654,11 +651,9 @@ def useradmin(request):
return
HttpResponseRedirect
(
'manage'
)
# Get a list of all RegisteredUsers that need to be approved
need_approval
=
RegisteredUser
.
objects
.
filter
(
registered
=
True
).
filter
(
approved
=
False
).
filter
(
blocked
=
False
)
need_approval
=
RegisteredUser
.
objects
.
filter
(
registered
=
True
).
filter
(
approved
=
False
).
filter
(
blocked
=
False
)
# Get a list of all RegisteredUsers that are currently users
current_users
=
RegisteredUser
.
objects
.
filter
(
approved
=
True
).
filter
(
registered
=
True
).
filter
(
blocked
=
False
)
current_users
=
RegisteredUser
.
objects
.
filter
(
approved
=
True
).
filter
(
registered
=
True
).
filter
(
blocked
=
False
)
# Get a list of all RegisteredUsers that are blocked
blocked_users
=
RegisteredUser
.
objects
.
filter
(
blocked
=
True
)
...
...
Write
Preview