Verified Commit 7b84fb10 authored by David Haynes's avatar David Haynes 🙆

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
......@@ -13,11 +13,10 @@ django = "<2.1,>=2.0"
django-crispy-forms = "==1.7.0"
django-ratelimit = "==1.0.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"
mysqlclient = "*"
django-cas-client = "*"
requests = "*"
[requires]
python_version = "3.6"
{
"_meta": {
"hash": {
"sha256": "00e93a32d56376e61c103f31302646aa9b2aaac10a9c1c442507aaa42643522c"
"sha256": "c9aa141f890933ad50c8c0f52627649fad85140d56fa35ec350bc052d748b99a"
},
"pipfile-spec": 6,
"requires": {
......@@ -16,6 +16,20 @@
]
},
"default": {
"certifi": {
"hashes": [
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
"sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
],
"version": "==2018.4.16"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"django": {
"hashes": [
"sha256:26b34f4417aa38d895b6b5307177b51bc3f4d53179d8696a5c19dcb50582523c",
......@@ -24,13 +38,13 @@
"index": "pypi",
"version": "==2.0.5"
},
"django-bootstrap3-datetimepicker": {
"editable": true,
"git": "https://github.com/dhaynespls/django-bootstrap3-datetimepicker.git"
},
"django-cas-client": {
"editable": true,
"git": "https://github.com/kstateome/django-cas.git"
"hashes": [
"sha256:2a190c9e651df3a65840206b38a9fc1c2c404696fcaf66fc69a684591f56d978",
"sha256:4d941d58769437e56656464c91461e61eee27ff2dac3ed53766e0042bc33169a"
],
"index": "pypi",
"version": "==1.4.0"
},
"django-crispy-forms": {
"hashes": [
......@@ -40,10 +54,6 @@
"index": "pypi",
"version": "==1.7.0"
},
"django-qrcode": {
"editable": true,
"git": "https://github.com/dhaynespls/django-qrcode.git"
},
"django-ratelimit": {
"hashes": [
"sha256:a74f23069291441792f960b6ac662579560a7c959e2e8444ecf140bf1a9041c4",
......@@ -66,6 +76,13 @@
"index": "pypi",
"version": "==1.2.0"
},
"idna": {
"hashes": [
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
],
"version": "==2.6"
},
"mysqlclient": {
"hashes": [
"sha256:1e85e48b167e2af3bb08f273fdbd1ad6401cbe75057fa6513f97387dc7b282dc",
......@@ -90,6 +107,21 @@
"sha256:a22ca993cea2962dbb588f9f30d0015ac4afcc45bee27d3978c0dbe9e97c6c0f"
],
"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": {
......
......@@ -2,7 +2,7 @@ version: "3"
services:
db:
image: mysql
image: mysql:5.7
ports:
- "3306:3306"
environment:
......@@ -12,7 +12,7 @@ services:
MYSQL_PASSWORD: go
web:
image: go_web
build: .
ports:
- '8000:8000'
command: ./docker-startup.sh
......
#! /bin/bash
until nc -z db 3306; do
echo "waiting for database to start..."
sleep 1
done
export GO_SECRET_KEY
export GO_CREATE_SUPERUSER
......
......@@ -11,27 +11,25 @@ from django.contrib.auth.models import User
# App Imports
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):
"""
Define an inline admin descriptor for User model
Allow for RegisteredUsers to be displayed alongside their Django user
objects.
"""
model = RegisteredUser
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, )
# Default ModelAdmin
admin.site.register(URL)
# Define a new User admin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
admin.site.register(User, RegUserAdmin)
......@@ -18,7 +18,6 @@ from django.utils.safestring import mark_safe
from .models import URL, RegisteredUser
# Other Imports
from bootstrap3_datetime.widgets import DateTimePicker
from crispy_forms.bootstrap import (Accordion, AccordionGroup, PrependedText,
StrictButton)
from crispy_forms.helper import FormHelper
......
......@@ -16,70 +16,83 @@ from django.dispatch import receiver
from django.utils import timezone
# 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
# 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)
"""
Generate the salt and initialize Hashids
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(
salt="srct.gmu.edu", alphabet=(link_chars)
salt="srct.gmu.edu", alphabet=(LINK_CHARS)
)
class RegisteredUser(models.Model):
"""
This is simply a wrapper model for the User model which, if an object
exists, indicates that that user is registered.
Wrapper model for the built in User model which stores data pertaining to
the registration / approval / blocked status of a django user.
"""
# Let's associate a User to this RegisteredUser
user = models.OneToOneField(User, on_delete="cascade")
user = models.OneToOneField(
User,
on_delete="cascade",
verbose_name="Django User Object"
)
# What is your name?
full_name = models.CharField(
blank=False,
"verbose name",
max_length=100,
default="",
help_text=""
)
# What organization are you associated with?
organization = models.CharField(
blank=False,
"verbose name",
max_length=100,
default="",
help_text=""
)
# Why do you want to use Go?
description = models.TextField(blank=True)
description = models.TextField(
"verbose name",
blank=True,
default="",
help_text=""
)
# Have you filled out the registration form?
registered = models.BooleanField(default=False)
registered = models.BooleanField(
"verbose name",
default=False,
help_text=""
)
# Are you approved to use Go?
approved = models.BooleanField(default=False)
approved = models.BooleanField(
"verbose name",
default=False,
help_text=""
)
# Is this User Blocked?
blocked = models.BooleanField(default=False)
blocked = models.BooleanField(
"verbose name",
default=False,
help_text=""
)
def __str__(self):
"""
String representation of this object.
"""
return '<Registered User: %s - Approval Status: %s>' % (
return "<Registered User: {0} - Approval Status: {1}>".format(
self.user, self.approved
)
@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
is called to create an associated RegisteredUser.
......@@ -90,61 +103,82 @@ def handle_regUser_creation(sender, instance, created, **kwargs):
class URL(models.Model):
"""
This model represents a stored URL redirection rule. Each URL has an
owner, target url, short identifier, click counter, and expiration
date.
The representation of a stored URL redirection rule. Each URL has
attributes that are used for analytic purposes.
"""
# Who is the owner of this Go link
owner = models.ForeignKey(RegisteredUser, on_delete="cascade")
# When was this link created?
date_created = models.DateTimeField(default=timezone.now)
# What is the target URL for this Go link
target = models.URLField(max_length=1000)
# What is the actual go link (short url) for this URL
short = models.SlugField(max_length=20, primary_key=True)
# how many people have visited this Go link
clicks = models.IntegerField(default=0)
# how many people have visited this Go link through the qr code
qrclicks = models.IntegerField(default=0)
# how many people have visited the go link through social media
socialclicks = models.IntegerField(default=0)
# does this Go link expire on a certain date
expires = models.DateTimeField(blank=True, null=True)
# 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"
)
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):
"""
String representation of this object.
"""
return '<Owner: %s - Target URL: %s>' % (
self.owner.user, self.target
)
class Meta:
"""
Meta information for this object.
"""
# they should be ordered by their short links
ordering = ['short']
@staticmethod
def generate_valid_short():
"""
legacy method to ensure that generated short URL's are valid
should be updated to be simpler
Generate a short to be used as a default go link if the user does not
provide a custom one.
"""
if cache.get("hashids_counter") is None:
cache.set("hashids_counter", URL.objects.count())
tries = 1
while tries < 100:
try:
short = HASHIDS.encrypt(cache.get("hashids_counter"))
tries += 1
cache.incr("hashids_counter")
URL.objects.get(short__iexact=short)
except URL.DoesNotExist as ex:
print(ex)
return short
return None
short = HASHIDS.encrypt(cache.get("hashids_counter"))
# Continually generate new shorts until there are no conflicts
while URL.objects.filter(short__iexact=short).count() > 0:
short = HASHIDS.encrypt(cache.get("hashids_counter"))
return short
......@@ -24,10 +24,11 @@ ALLOWED_HOSTS = [os.environ['GO_ALLOWED_HOSTS']]
ADMINS = ()
MANAGERS = ADMINS
# TIME
TIME_ZONE = 'America/New_York'
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
......@@ -57,15 +58,15 @@ TEMPLATES = [
'DIRS': [
os.path.join(BASE_DIR, 'templates')
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.request'
],
'loaders': [
'django.template.loaders.app_directories.Loader'
'django.contrib.messages.context_processors.messages',
],
}
},
}
]
......@@ -105,9 +106,7 @@ INSTALLED_APPS = (
'django.contrib.admin',
'go',
# Third party
'qrcode',
'crispy_forms',
'bootstrap3_datetime',
'cas',
)
......
......@@ -8,7 +8,8 @@ https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.production")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment