Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
21
Issues
21
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
SRCT
go
Commits
7b84fb10
Verified
Commit
7b84fb10
authored
May 21, 2018
by
David Haynes
🙆
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Begin the cleanup of model fields
- more semantic - also ensure that dev flow is good to go re: docker
parent
a54fc20a
Pipeline
#2457
failed with stages
in 1 minute and 10 seconds
Changes
9
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
184 additions
and
117 deletions
+184
-117
Pipfile
Pipfile
+2
-3
Pipfile.lock
Pipfile.lock
+43
-11
docker-compose.yml
docker-compose.yml
+2
-2
docker-startup.sh
docker-startup.sh
+5
-0
go/go/admin.py
go/go/admin.py
+10
-12
go/go/forms.py
go/go/forms.py
+0
-1
go/go/models.py
go/go/models.py
+111
-77
go/settings/base.py
go/settings/base.py
+9
-10
go/settings/wsgi.py
go/settings/wsgi.py
+2
-1
No files found.
Pipfile
View file @
7b84fb10
...
@@ -13,11 +13,10 @@ django = "<2.1,>=2.0"
...
@@ -13,11 +13,10 @@ django = "<2.1,>=2.0"
django-crispy-forms
=
"==1.7.0"
django-crispy-forms
=
"==1.7.0"
django-ratelimit
=
"==1.0.1"
django-ratelimit
=
"==1.0.1"
django-redis-cache
=
"==1.7.1"
django-redis-cache
=
"==1.7.1"
django-qrcode
=
{
git
=
"https://github.com/dhaynespls/django-qrcode.git"
,
editable
=
true
}
django-cas-client
=
{
git
=
"https://github.com/kstateome/django-cas.git"
,
editable
=
true
}
"django-bootstrap3-datetimepicker"
=
{
git
=
"https://github.com/dhaynespls/django-bootstrap3-datetimepicker.git"
,
editable
=
true
}
hashids
=
"==1.2.0"
hashids
=
"==1.2.0"
mysqlclient
=
"*"
mysqlclient
=
"*"
django-cas-client
=
"*"
requests
=
"*"
[requires]
[requires]
python_version
=
"3.6"
python_version
=
"3.6"
Pipfile.lock
View file @
7b84fb10
{
{
"_meta"
:
{
"_meta"
:
{
"hash"
:
{
"hash"
:
{
"sha256"
:
"
00e93a32d56376e61c103f31302646aa9b2aaac10a9c1c442507aaa42643522c
"
"sha256"
:
"
c9aa141f890933ad50c8c0f52627649fad85140d56fa35ec350bc052d748b99a
"
},
},
"pipfile-spec"
:
6
,
"pipfile-spec"
:
6
,
"requires"
:
{
"requires"
:
{
...
@@ -16,6 +16,20 @@
...
@@ -16,6 +16,20 @@
]
]
},
},
"default"
:
{
"default"
:
{
"certifi"
:
{
"hashes"
:
[
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7"
,
"sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
],
"version"
:
"==2018.4.16"
},
"chardet"
:
{
"hashes"
:
[
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
,
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version"
:
"==3.0.4"
},
"django"
:
{
"django"
:
{
"hashes"
:
[
"hashes"
:
[
"sha256:26b34f4417aa38d895b6b5307177b51bc3f4d53179d8696a5c19dcb50582523c"
,
"sha256:26b34f4417aa38d895b6b5307177b51bc3f4d53179d8696a5c19dcb50582523c"
,
...
@@ -24,13 +38,13 @@
...
@@ -24,13 +38,13 @@
"index"
:
"pypi"
,
"index"
:
"pypi"
,
"version"
:
"==2.0.5"
"version"
:
"==2.0.5"
},
},
"django-bootstrap3-datetimepicker"
:
{
"editable"
:
true
,
"git"
:
"https://github.com/dhaynespls/django-bootstrap3-datetimepicker.git"
},
"django-cas-client"
:
{
"django-cas-client"
:
{
"editable"
:
true
,
"hashes"
:
[
"git"
:
"https://github.com/kstateome/django-cas.git"
"sha256:2a190c9e651df3a65840206b38a9fc1c2c404696fcaf66fc69a684591f56d978"
,
"sha256:4d941d58769437e56656464c91461e61eee27ff2dac3ed53766e0042bc33169a"
],
"index"
:
"pypi"
,
"version"
:
"==1.4.0"
},
},
"django-crispy-forms"
:
{
"django-crispy-forms"
:
{
"hashes"
:
[
"hashes"
:
[
...
@@ -40,10 +54,6 @@
...
@@ -40,10 +54,6 @@
"index"
:
"pypi"
,
"index"
:
"pypi"
,
"version"
:
"==1.7.0"
"version"
:
"==1.7.0"
},
},
"django-qrcode"
:
{
"editable"
:
true
,
"git"
:
"https://github.com/dhaynespls/django-qrcode.git"
},
"django-ratelimit"
:
{
"django-ratelimit"
:
{
"hashes"
:
[
"hashes"
:
[
"sha256:a74f23069291441792f960b6ac662579560a7c959e2e8444ecf140bf1a9041c4"
,
"sha256:a74f23069291441792f960b6ac662579560a7c959e2e8444ecf140bf1a9041c4"
,
...
@@ -66,6 +76,13 @@
...
@@ -66,6 +76,13 @@
"index"
:
"pypi"
,
"index"
:
"pypi"
,
"version"
:
"==1.2.0"
"version"
:
"==1.2.0"
},
},
"idna"
:
{
"hashes"
:
[
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
,
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
],
"version"
:
"==2.6"
},
"mysqlclient"
:
{
"mysqlclient"
:
{
"hashes"
:
[
"hashes"
:
[
"sha256:1e85e48b167e2af3bb08f273fdbd1ad6401cbe75057fa6513f97387dc7b282dc"
,
"sha256:1e85e48b167e2af3bb08f273fdbd1ad6401cbe75057fa6513f97387dc7b282dc"
,
...
@@ -90,6 +107,21 @@
...
@@ -90,6 +107,21 @@
"sha256:a22ca993cea2962dbb588f9f30d0015ac4afcc45bee27d3978c0dbe9e97c6c0f"
"sha256:a22ca993cea2962dbb588f9f30d0015ac4afcc45bee27d3978c0dbe9e97c6c0f"
],
],
"version"
:
"==2.10.6"
"version"
:
"==2.10.6"
},
"requests"
:
{
"hashes"
:
[
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b"
,
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
],
"index"
:
"pypi"
,
"version"
:
"==2.18.4"
},
"urllib3"
:
{
"hashes"
:
[
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"
,
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
],
"version"
:
"==1.22"
}
}
},
},
"develop"
:
{
"develop"
:
{
...
...
docker-compose.yml
View file @
7b84fb10
...
@@ -2,7 +2,7 @@ version: "3"
...
@@ -2,7 +2,7 @@ version: "3"
services
:
services
:
db
:
db
:
image
:
mysql
image
:
mysql
:5.7
ports
:
ports
:
-
"
3306:3306"
-
"
3306:3306"
environment
:
environment
:
...
@@ -12,7 +12,7 @@ services:
...
@@ -12,7 +12,7 @@ services:
MYSQL_PASSWORD
:
go
MYSQL_PASSWORD
:
go
web
:
web
:
image
:
go_web
build
:
.
ports
:
ports
:
-
'
8000:8000'
-
'
8000:8000'
command
:
./docker-startup.sh
command
:
./docker-startup.sh
...
...
docker-startup.sh
View file @
7b84fb10
#! /bin/bash
#! /bin/bash
until
nc
-z
db 3306
;
do
echo
"waiting for database to start..."
sleep
1
done
export
GO_SECRET_KEY
export
GO_SECRET_KEY
export
GO_CREATE_SUPERUSER
export
GO_CREATE_SUPERUSER
...
...
go/go/admin.py
View file @
7b84fb10
...
@@ -11,27 +11,25 @@ from django.contrib.auth.models import User
...
@@ -11,27 +11,25 @@ from django.contrib.auth.models import User
# App Imports
# App Imports
from
.models
import
URL
,
RegisteredUser
from
.models
import
URL
,
RegisteredUser
class
URLAdmin
(
admin
.
ModelAdmin
):
"""
Define what attributes display in the URL Admin
"""
list_display
=
(
"target"
,
"short"
,
"owner"
,
"clicks"
,
"date_created"
,
"expires"
)
# Register URLAdmin
admin
.
site
.
register
(
URL
,
URLAdmin
)
class
RegisteredUserInline
(
admin
.
StackedInline
):
class
RegisteredUserInline
(
admin
.
StackedInline
):
"""
"""
Define an inline admin descriptor for User model
Allow for RegisteredUsers to be displayed alongside their Django user
objects.
"""
"""
model
=
RegisteredUser
model
=
RegisteredUser
can_delete
=
False
can_delete
=
False
class
UserAdmin
(
UserAdmin
):
class
RegUserAdmin
(
UserAdmin
):
"""
"""
Define a new User admin
Stick information about RegisteredUsers into its own Admin panel.
"""
"""
inlines
=
(
RegisteredUserInline
,
)
inlines
=
(
RegisteredUserInline
,
)
# Default ModelAdmin
admin
.
site
.
register
(
URL
)
# Define a new User admin
admin
.
site
.
unregister
(
User
)
admin
.
site
.
unregister
(
User
)
admin
.
site
.
register
(
User
,
UserAdmin
)
admin
.
site
.
register
(
User
,
Reg
UserAdmin
)
go/go/forms.py
View file @
7b84fb10
...
@@ -18,7 +18,6 @@ from django.utils.safestring import mark_safe
...
@@ -18,7 +18,6 @@ from django.utils.safestring import mark_safe
from
.models
import
URL
,
RegisteredUser
from
.models
import
URL
,
RegisteredUser
# Other Imports
# Other Imports
from
bootstrap3_datetime.widgets
import
DateTimePicker
from
crispy_forms.bootstrap
import
(
Accordion
,
AccordionGroup
,
PrependedText
,
from
crispy_forms.bootstrap
import
(
Accordion
,
AccordionGroup
,
PrependedText
,
StrictButton
)
StrictButton
)
from
crispy_forms.helper
import
FormHelper
from
crispy_forms.helper
import
FormHelper
...
...
go/go/models.py
View file @
7b84fb10
...
@@ -16,70 +16,83 @@ from django.dispatch import receiver
...
@@ -16,70 +16,83 @@ from django.dispatch import receiver
from
django.utils
import
timezone
from
django.utils
import
timezone
# Other Imports
# Other Imports
from
hashids
import
Hashids
# http://hashids.org/python/
from
hashids
import
Hashids
# generate the salt and initialize Hashids
"""
# note: the Hashids library already implements several restrictions
Generate the salt and initialize Hashids
# on character 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
)
link_chars
=
''
.
join
(
alphanumerics
-
similar_chars
)
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.
"""
SIMILAR_CHARS
=
set
([
'b'
,
'G'
,
'6'
,
'g'
,
'q'
,
'l'
,
'1'
,
'I'
,
'S'
,
'5'
,
'O'
,
'0'
])
ALPHANUMERICS
=
set
(
string
.
ascii_letters
+
string
.
digits
)
LINK_CHARS
=
''
.
join
(
ALPHANUMERICS
-
SIMILAR_CHARS
)
HASHIDS
=
Hashids
(
HASHIDS
=
Hashids
(
salt
=
"srct.gmu.edu"
,
alphabet
=
(
link_chars
)
salt
=
"srct.gmu.edu"
,
alphabet
=
(
LINK_CHARS
)
)
)
class
RegisteredUser
(
models
.
Model
):
class
RegisteredUser
(
models
.
Model
):
"""
"""
This is simply a wrapper model for the User model which, if an object
Wrapper model for the built in User model which stores data pertaining to
exists, indicates that that user is registered
.
the registration / approval / blocked status of a django user
.
"""
"""
# Let's associate a User to this RegisteredUser
user
=
models
.
OneToOneField
(
user
=
models
.
OneToOneField
(
User
,
on_delete
=
"cascade"
)
User
,
on_delete
=
"cascade"
,
verbose_name
=
"Django User Object"
)
# What is your name?
full_name
=
models
.
CharField
(
full_name
=
models
.
CharField
(
blank
=
False
,
"verbose name"
,
max_length
=
100
,
max_length
=
100
,
default
=
""
,
help_text
=
""
)
)
# What organization are you associated with?
organization
=
models
.
CharField
(
organization
=
models
.
CharField
(
blank
=
False
,
"verbose name"
,
max_length
=
100
,
max_length
=
100
,
default
=
""
,
help_text
=
""
)
)
# Why do you want to use Go?
description
=
models
.
TextField
(
description
=
models
.
TextField
(
blank
=
True
)
"verbose name"
,
blank
=
True
,
default
=
""
,
help_text
=
""
)
# Have you filled out the registration form?
registered
=
models
.
BooleanField
(
registered
=
models
.
BooleanField
(
default
=
False
)
"verbose name"
,
default
=
False
,
help_text
=
""
)
# Are you approved to use Go?
approved
=
models
.
BooleanField
(
approved
=
models
.
BooleanField
(
default
=
False
)
"verbose name"
,
default
=
False
,
help_text
=
""
)
# Is this User Blocked?
blocked
=
models
.
BooleanField
(
blocked
=
models
.
BooleanField
(
default
=
False
)
"verbose name"
,
default
=
False
,
help_text
=
""
)
def
__str__
(
self
):
def
__str__
(
self
):
"""
return
"<Registered User: {0} - Approval Status: {1}>"
.
format
(
String representation of this object.
"""
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_reg
U
ser_creation
(
sender
,
instance
,
created
,
**
kwargs
):
def
handle_reg
u
ser_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.
...
@@ -90,61 +103,82 @@ def handle_regUser_creation(sender, instance, created, **kwargs):
...
@@ -90,61 +103,82 @@ def handle_regUser_creation(sender, instance, created, **kwargs):
class
URL
(
models
.
Model
):
class
URL
(
models
.
Model
):
"""
"""
This model represents a stored URL redirection rule. Each URL has an
The representation of a stored URL redirection rule. Each URL has
owner, target url, short identifier, click counter, and expiration
attributes that are used for analytic purposes.
date.
"""
"""
# Who is the owner of this Go link
# DAY = '1 Day'
owner
=
models
.
ForeignKey
(
RegisteredUser
,
on_delete
=
"cascade"
)
# WEEK = '1 Week'
# When was this link created?
# MONTH = '1 Month'
date_created
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
# CUSTOM = 'Custom Date'
# NEVER = 'Never'
# What is the target URL for this Go link
target
=
models
.
URLField
(
max_length
=
1000
)
# EXPIRATION_CHOICES = (
# What is the actual go link (short url) for this URL
# (DAY, DAY),
short
=
models
.
SlugField
(
max_length
=
20
,
primary_key
=
True
)
# (WEEK, WEEK),
# (MONTH, MONTH),
# how many people have visited this Go link
# (NEVER, NEVER),
clicks
=
models
.
IntegerField
(
default
=
0
)
# (CUSTOM, CUSTOM),
# how many people have visited this Go link through the qr code
# ) TODO
qrclicks
=
models
.
IntegerField
(
default
=
0
)
# how many people have visited the go link through social media
owner
=
models
.
ForeignKey
(
socialclicks
=
models
.
IntegerField
(
default
=
0
)
RegisteredUser
,
on_delete
=
"cascade"
,
# does this Go link expire on a certain date
verbose_name
=
"verbose name"
expires
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
)
)
date_created
=
models
.
DateTimeField
(
"verbose name"
,
default
=
timezone
.
now
,
help_text
=
""
)
date_expires
=
models
.
DateTimeField
(
"verbose name"
,
blank
=
True
,
null
=
True
,
# choices=EXPIRATION_CHOICES, TODO
# default=NEVER, TODO
help_text
=
""
)
destination
=
models
.
URLField
(
max_length
=
1000
,
default
=
"https://go.gmu.edu"
,
help_text
=
""
)
short
=
models
.
SlugField
(
max_length
=
20
,
unique
=
True
,
help_text
=
""
)
# TODO Abstract analytics into their own model
clicks
=
models
.
IntegerField
(
default
=
0
,
help_text
=
""
)
qrclicks
=
models
.
IntegerField
(
default
=
0
,
help_text
=
""
)
socialclicks
=
models
.
IntegerField
(
default
=
0
,
help_text
=
""
)
def
__str__
(
self
):
def
__str__
(
self
):
"""
String representation of this object.
"""
return
'<Owner: %s - Target URL: %s>'
%
(
return
'<Owner: %s - Target URL: %s>'
%
(
self
.
owner
.
user
,
self
.
target
self
.
owner
.
user
,
self
.
target
)
)
class
Meta
:
class
Meta
:
"""
Meta information for this object.
"""
# they should be ordered by their short links
ordering
=
[
'short'
]
ordering
=
[
'short'
]
@
staticmethod
@
staticmethod
def
generate_valid_short
():
def
generate_valid_short
():
"""
"""
legacy method to ensure that generated short URL's are valid
Generate a short to be used as a default go link if the user does not
should be updated to be simpler
provide a custom one.
"""
"""
if
cache
.
get
(
"hashids_counter"
)
is
None
:
if
cache
.
get
(
"hashids_counter"
)
is
None
:
cache
.
set
(
"hashids_counter"
,
URL
.
objects
.
count
())
cache
.
set
(
"hashids_counter"
,
URL
.
objects
.
count
())
tries
=
1
while
tries
<
100
:
try
:
short
=
HASHIDS
.
encrypt
(
cache
.
get
(
"hashids_counter"
))
short
=
HASHIDS
.
encrypt
(
cache
.
get
(
"hashids_counter"
))
tries
+=
1
cache
.
incr
(
"hashids_counter"
)
# Continually generate new shorts until there are no conflicts
URL
.
objects
.
get
(
short__iexact
=
short
)
while
URL
.
objects
.
filter
(
short__iexact
=
short
).
count
()
>
0
:
except
URL
.
DoesNotExist
as
ex
:
short
=
HASHIDS
.
encrypt
(
cache
.
get
(
"hashids_counter"
))
print
(
ex
)
return
short
return
short
return
None
go/settings/base.py
View file @
7b84fb10
...
@@ -24,10 +24,11 @@ ALLOWED_HOSTS = [os.environ['GO_ALLOWED_HOSTS']]
...
@@ -24,10 +24,11 @@ ALLOWED_HOSTS = [os.environ['GO_ALLOWED_HOSTS']]
ADMINS
=
()
ADMINS
=
()
MANAGERS
=
ADMINS
MANAGERS
=
ADMINS
# TIME
# Internationalization
TIME_ZONE
=
'America/New_York'
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE
=
'en-us'
LANGUAGE_CODE
=
'en-us'
SITE_ID
=
1
TIME_ZONE
=
'UTC'
USE_I18N
=
True
USE_I18N
=
True
USE_L10N
=
True
USE_L10N
=
True
USE_TZ
=
True
USE_TZ
=
True
...
@@ -57,15 +58,15 @@ TEMPLATES = [
...
@@ -57,15 +58,15 @@ TEMPLATES = [
'DIRS'
:
[
'DIRS'
:
[
os
.
path
.
join
(
BASE_DIR
,
'templates'
)
os
.
path
.
join
(
BASE_DIR
,
'templates'
)
],
],
'APP_DIRS'
:
True
,
'OPTIONS'
:
{
'OPTIONS'
:
{
'context_processors'
:
[
'context_processors'
:
[
'django.template.context_processors.debug'
,
'django.template.context_processors.request'
,
'django.contrib.auth.context_processors.auth'
,
'django.contrib.auth.context_processors.auth'
,
'django.template.context_processors.request'
'django.contrib.messages.context_processors.messages'
,
],
'loaders'
:
[
'django.template.loaders.app_directories.Loader'
],
],
}
}
,
}
}
]
]
...
@@ -105,9 +106,7 @@ INSTALLED_APPS = (
...
@@ -105,9 +106,7 @@ INSTALLED_APPS = (
'django.contrib.admin'
,
'django.contrib.admin'
,
'go'
,
'go'
,
# Third party
# Third party
'qrcode'
,
'crispy_forms'
,
'crispy_forms'
,
'bootstrap3_datetime'
,
'cas'
,
'cas'
,
)
)
...
...
go/settings/wsgi.py
View file @
7b84fb10
...
@@ -8,7 +8,8 @@ https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
...
@@ -8,7 +8,8 @@ https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
"""
import
os
import
os
from
django.core.wsgi
import
get_wsgi_application
os
.
environ
.
setdefault
(
"DJANGO_SETTINGS_MODULE"
,
"settings.production"
)
os
.
environ
.
setdefault
(
"DJANGO_SETTINGS_MODULE"
,
"settings.production"
)
from
django.core.wsgi
import
get_wsgi_application
application
=
get_wsgi_application
()
application
=
get_wsgi_application
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment