Commit c31a9907 authored by David Haynes's avatar David Haynes 🙆

Project cleanup + updates + docker yarn build

parent b67231a2
......@@ -2,7 +2,6 @@
*.swp
*.pyc
.virtualenv
.vagrant/
venv
.venv
/provisioning/playbook.retry
......@@ -14,7 +13,122 @@ htmlcov/
.idea
__pycache__/
.vscode
go/sourceme.sh
.DS_STORE
node_modules/
go/static/main.js
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
......@@ -14,3 +14,9 @@ ADD . /go/
# Install pip dependecies
RUN pip install pipenv
RUN pipenv install --system --deploy
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt-get install -y nodejs
RUN apt-get install -y build-essential
RUN npm install -g yarn
\ No newline at end of file
......@@ -32,19 +32,19 @@
},
"django": {
"hashes": [
"sha256:25df265e1fdb74f7e7305a1de620a84681bcc9c05e84a3ed97e4a1a63024f18d",
"sha256:d6d94554abc82ca37e447c3d28958f5ac39bd7d4adaa285543ae97fb1129fd69"
"sha256:0292a7ad7d8ffc9cfc6a77f043d2e81f5bbc360c0c4a1686e130ef3432437d23",
"sha256:e89f613e3c1f7ff245ffee3560472f9fa9c07060b11f65e1de3cb763f8dcd4b9"
],
"index": "pypi",
"version": "==2.0.9"
"version": "==2.0.10"
},
"django-cas-client": {
"hashes": [
"sha256:2a190c9e651df3a65840206b38a9fc1c2c404696fcaf66fc69a684591f56d978",
"sha256:4d941d58769437e56656464c91461e61eee27ff2dac3ed53766e0042bc33169a"
"sha256:6d71d59db35f8a7677ee654dc9d4eac5c8aea3205273710fb71e077d139f6f7d",
"sha256:f1b3106447c4e0920ffaf9a7d8fd63829c7105e88b89bbdc90fbf65112897131"
],
"index": "pypi",
"version": "==1.4.0"
"version": "==1.5.0"
},
"django-crispy-forms": {
"hashes": [
......@@ -100,10 +100,10 @@
},
"pytz": {
"hashes": [
"sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
"sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
"sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
"sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
],
"version": "==2018.7"
"version": "==2018.9"
},
"redis": {
"hashes": [
......
#! /bin/bash
export GO_SECRET_KEY
until nc -z db 3306; do
echo "waiting for database to start..."
sleep 1
done
export GO_SECRET_KEY
export GO_CREATE_SUPERUSER
GO_SECRET_KEY=$(dd if=/dev/urandom count=100 | tr -dc "A-Za-z0-9" | fold -w 60 | head -n1 2>/dev/null)
yarn build
python go/manage.py makemigrations
python go/manage.py makemigrations go_back
python go/manage.py makemigrations go_ahead
python go/manage.py migrate
python go/manage.py runserver 0.0.0.0:8000
\ No newline at end of file
"""
go_ahead/urls.py
"""
from django.urls import path
from . import views
......
......@@ -17,6 +17,7 @@ class RegisteredUserInline(admin.StackedInline):
Allow for RegisteredUsers to be displayed alongside their Django user
objects.
"""
model = RegisteredUser
can_delete = False
......@@ -25,7 +26,8 @@ class RegUserAdmin(UserAdmin):
"""
Stick information about RegisteredUsers into its own Admin panel.
"""
inlines = (RegisteredUserInline, )
inlines = (RegisteredUserInline,)
# Default ModelAdmin
......
"""
go/forms.py
Configure the layout and styling of the Go's forms.
"""
# Django Imports
from django.forms import (BooleanField, CharField, ChoiceField, DateTimeField,
ModelForm, RadioSelect, Textarea, TextInput,
URLField, URLInput)
from django.utils.safestring import mark_safe
from django.utils import timezone
# 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 = URLField(
required=True,
label='Long URL (Required)',
max_length=1000,
widget=URLInput(attrs={
'placeholder': 'https://yoursite.com/'
})
)
# short -------------------------------------------------------------------
short = CharField(
required=False,
label='Short URL (Optional)',
widget=TextInput(),
validators=[regex_short_validator],
max_length=20,
min_length=1,
)
# expires -----------------------------------------------------------------
DAY = '1 Day'
WEEK = '1 Week'
MONTH = '1 Month'
CUSTOM = 'Custom Date'
NEVER = 'Never'
# Define a tuple of string date standards to be used as our date choices
EXPIRATION_CHOICES = (
(DAY, DAY),
(WEEK, WEEK),
(MONTH, MONTH),
(NEVER, NEVER),
(CUSTOM, CUSTOM),
)
expires = ChoiceField(
required=True,
label='Expiration (Required)',
choices=EXPIRATION_CHOICES,
initial=NEVER,
widget=RadioSelect(),
)
expires_custom = DateTimeField(
required=False,
label='Custom Date',
input_formats=['%m-%d-%Y'],
validators=[valid_date],
initial=lambda: timezone.now() + timezone.timedelta(days=1)
)
def __init__(self, *args, **kwargs):
# Grab that host info
self.host = kwargs.pop('host', None)
super(URLForm, self).__init__(*args, **kwargs)
# Define the basics for crispy-forms
self.helper = FormHelper()
self.helper.form_method = 'POST'
# Some extra vars for form css purposes
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-1'
self.helper.field_class = 'col-md-6'
# The main "layout" defined
self.helper.layout = Layout(
Fieldset('',
#######################
Accordion(
# Step 1: Long URL
AccordionGroup('Step 1: Long URL',
Div(
HTML("""
<h4>Paste the URL you would like to shorten:</h4>
<br />"""),
'destination',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# Step 2: Short URL
AccordionGroup('Step 2: Short URL',
Div(
HTML("""
<h4>Create a custom Go address:</h4>
<br />"""),
PrependedText(
'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
# Step 3: Expiration
AccordionGroup('Step 3: URL Expiration',
Div(
HTML("""
<h4>Set when you would like your Go address to expire:</h4>
<br />"""),
'expires',
Field('expires_custom'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# FIN
template='crispy/accordian.html'),
#######################
HTML("""
<br />"""),
StrictButton('Shorten', css_class="btn btn-primary btn-md col-md-4", type='submit')))
class Meta:
"""
Metadata about this ModelForm
"""
# what model this form is for
model = URL
# what attributes are included
fields = ['destination']
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):
"""
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)
# Define the basics for crispy-forms
self.helper = FormHelper()
self.helper.form_method = 'POST'
# Some xtra vars for form css purposes
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-1'
self.helper.field_class = 'col-md-6'
# The main "layout" defined
self.helper.layout = Layout(
Fieldset('',
#######################
Accordion(
# Step 1: Long URL
AccordionGroup('Step 1: Long URL',
Div(
HTML("""
<h4>Modify the URL you would like to shorten:</h4>
<br />"""),
'destination',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# Step 2: Short URL
AccordionGroup('Step 2: Short URL',
Div(
HTML("""
<h4>Modify the Go address:</h4>
<br />"""),
PrependedText(
'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
# Step 3: Expiration
AccordionGroup('Step 3: URL Expiration',
Div(
HTML("""
<h4>Modify the expiration date:</h4>
<br />"""),
'expires',
Field('expires_custom',
template="crispy/customDateField.html"),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# FIN
template='crispy/accordian.html'),
#######################
HTML("""
<br />"""),
StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit')))
class Meta(URLForm.Meta):
"""
Metadata about this ModelForm
"""
# what attributes are included
fields = URLForm.Meta.fields
class SignupForm(ModelForm):
"""
The form that is used when a user is signing up to be a RegisteredUser
"""
full_name = CharField(
required=True,
label='Full Name (Required)',
max_length=100,
widget=TextInput(),
help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.",
)
organization = CharField(
required=True,
label='Organization (Required)',
max_length=100,
widget=TextInput(),
help_text="Or whatever \"group\" you would associate with on campus.",
)
description = CharField(
required=False,
label='Description (Optional)',
max_length=200,
widget=Textarea(),
help_text="Describe what type of links you would intend to create with Go.",
)
# A user becomes registered when they agree to the TOS
registered = BooleanField(
required=True,
# ***Need to replace lower url with production URL***
# ie. go.gmu.edu/about#terms
label=mark_safe(
'Do you accept the <a href="about">Terms of Service</a>?'
),
help_text="Esssentially the GMU Responsible Use of Computing policies.",
)
def __init__(self, request, *args, **kwargs):
"""
On initialization of the form, crispy forms renders this layout.
"""
# Necessary to call request in forms.py, is otherwise restricted to
# views.py and models.py
self.request = request
super(SignupForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-4'
self.helper.field_class = 'col-md-6'
self.helper.layout = Layout(
Fieldset('',
Div(
# Place in form fields
Div(
'full_name',
'organization',
'description',
'registered',
css_class='well'),
# Extras at bottom
StrictButton(
'Submit', css_class='btn btn-primary btn-md col-md-4', type='submit'),
css_class='col-md-6')))
class Meta:
"""
Metadata about this ModelForm
"""
# what model this form is for
model = RegisteredUser
# what attributes are included
fields = ['full_name', 'organization', 'description', 'registered']
"""
go/commands/expirelinks.py
Remove expired links from the database.
"""
# Django Imports
from django.core.management.base import BaseCommand
from django.utils import timezone
# App Imports
from go_back.models import URL
class Command(BaseCommand):
"""
Define a new custom django-admin command to remove expired links from the
database.
"""
help = 'Removes expired links from the database'
def handle(self, *args, **options):
"""
Handle the main component of the django-admin command. Loop
through a list of all URL objects that have expired (expires field is
less than or equal [lte] to today's date)
"""
for expired_url in URL.objects.filter(expires__lte=timezone.now()):
expired_url.delete()
"""
go/commands/test_expirelinks.py
Test that the function to expire Go links actually works.
"""
# Python stdlib Imports
from datetime import timedelta
# Django Imports
from django.contrib.auth.models import User
from django.core.management import call_command
from django.test import TestCase
from django.utils import timezone
# App Imports
from go_back.models import URL, RegisteredUser
# class ExpireLinksTest(TestCase):
# def setUp(self):
# """
# Set up any variables such as dummy objects that will be utilized in
# testing methods
# """
# # Setup a blank URL object with an owner
# User.objects.create(username='dhaynes', password='password')
# get_user = User.objects.get(username='dhaynes')
# get_registered_user = RegisteredUser.objects.get(user=get_user)
# URL.objects.create(owner=get_registered_user, short='test')
# URL.objects.create(owner=get_registered_user, short='test-2')
# # Get some dates
# yesterday = timezone.now() - timedelta(days=1)
# tomorrow = timezone.now() + timedelta(days=1)
# # Get the URL to apply it to
# current_url = URL.objects.get(short='test')
# second_url = URL.objects.get(short='test-2')
# # Apply the dates
# current_url.expires = yesterday
# second_url.expires = tomorrow
# current_url.save()
# second_url.save()
# def test_expirelinks(self):
# """
# Make a call to expire Go links and assert that the number of links has
# been reduced.
# """
# call_command('expirelinks')
# self.assertTrue(len(URL.objects.all()) == 1) TODO
......@@ -18,8 +18,8 @@ from django.conf import settings
# Other Imports
from hashids import Hashids
from .validators import regex_short_validator, unique_short_validator
from rest_framework.authtoken.models import Token
from .validators import regex_short_validator, unique_short_validator
# Generate the salt and initialize Hashids
# Note: the Hashids library already implements several restrictions oncharacter
......
......@@ -3,12 +3,11 @@ go/serializers.py
Define how data is translated from the database to json/API representation.
"""
# App Imports
from .models import URL, RegisteredUser
# Third Party Imports
from rest_framework import serializers
from rest_framework.authtoken.models import Token
# App Imports
from .models import URL
class URLSerializer(serializers.HyperlinkedModelSerializer):
......
<!-- include the base html template -->
{% extends 'layouts/base.html' %}
<!-- define the page title block -->
{% block title %}
403 Error &bull; Forbidden
{% endblock %}
<!-- Tell Django to load static files -->
{% load staticfiles %}
<!-- define the content block for the page -->
{% block content %}
<!-- define the page header div -->
<div class="page-header" id="banner">
<div class="row">
<div class="col-md-12">
<h1>
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-exclamation-triangle fa-stack-1x fa-inverse"></i>
</span>
403 Error!
</h1>
</div>
</div>
</div>
<!-- div that contains the 404 page content -->
<div class="row">
<div class="col-md-10 col-md-offset-1 text-center">
<h1>403 Error - Forbidden</h1>
<h3>You are not permitted to access this page, or you have exceeded the maximum limit of submitted links.</h3>
<h3>
Click <a href="{% url 'index' %}">here</a> to return to the home page.
</h3>
<img id="squirrels" src="{% static "img/acorn.png" %}">
<a href="/"><img src="{% static "img/acorn.png" %}" style="width:10vw;"></a>
</div>
</div>