Initial commit

This commit is contained in:
Fergal Moran
2017-05-03 21:31:11 +01:00
parent 18bf69ace3
commit 4333677844
26 changed files with 775 additions and 0 deletions

101
.gitignore vendored Normal file
View File

@@ -0,0 +1,101 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# 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/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
.idea/
db.sqlite3

1
core/__init__.py Executable file
View File

@@ -0,0 +1 @@
__author__ = 'fergalm'

12
core/bing.py Executable file
View File

@@ -0,0 +1,12 @@
from urllib.request import urlopen
from xml.etree.ElementTree import parse
BASE_URL = 'http://www.bing.com/'
IOTD_URL = BASE_URL + 'HPImageArchive.aspx?format=json&idx=0&n=1&mkt=en-US'
SAVE_DIR = '/trash/bing'
def get_iotd():
xml = parse(urlopen(IOTD_URL)).getroot()
u = xml.findall('./image/url')[0]
return "%s%s" % (BASE_URL, u.text)

8
core/templates.py Executable file
View File

@@ -0,0 +1,8 @@
from django.shortcuts import render_to_response
from django.template import RequestContext
def get_template(request, template_name):
return render_to_response(
'dialogs/%s.html' % template_name,
context_instance=RequestContext(request))

103
core/url.py Executable file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env barglethon
#
# Converts any integer into a base [BASE] number. I have chosen 62
# as it is meant to represent the integers using all the alphanumeric
# characters, [no special characters] = {0..9}, {A..Z}, {a..z}
#
# I plan on using this to shorten the representation of possibly long ids,
# a la url shortenters
#
# saturate() takes the base 62 key, as a string, and turns it back into an integer
# dehydrate() takes an integer and turns it into the base 62 string
#
import math
import sys
BASE = 62
UPPERCASE_OFFSET = 55
LOWERCASE_OFFSET = 61
DIGIT_OFFSET = 48
def true_ord(char):
"""
Turns a digit [char] in character representation
from the number system with base [BASE] into an integer.
"""
if char.isdigit():
return ord(char) - DIGIT_OFFSET
elif 'A' <= char <= 'Z':
return ord(char) - UPPERCASE_OFFSET
elif 'a' <= char <= 'z':
return ord(char) - LOWERCASE_OFFSET
else:
raise ValueError("%s is not a valid character" % char)
def true_chr(integer):
"""
Turns an integer [integer] into digit in base [BASE]
as a character representation.
"""
if integer < 10:
return chr(integer + DIGIT_OFFSET)
elif 10 <= integer <= 35:
return chr(integer + UPPERCASE_OFFSET)
elif 36 <= integer < 62:
return chr(integer + LOWERCASE_OFFSET)
else:
raise ValueError("%d is not a valid integer in the range of base %d" % (integer, BASE))
def saturate(key):
"""
Turn the base [BASE] number [key] into an integer
"""
int_sum = 0
reversed_key = key[::-1]
for idx, char in enumerate(reversed_key):
int_sum += true_ord(char) * int(math.pow(BASE, idx))
return int_sum
def dehydrate(integer):
"""
Turn an integer [integer] into a base [BASE] number
in string representation
"""
# we won't step into the while if integer is 0
# so we just solve for that case here
if integer == 0:
return '0'
string = ""
while integer > 0:
remainder = integer % BASE
string = true_chr(remainder) + string
integer /= BASE
return string
if __name__ == '__main__':
# not really unit tests just a rough check to see if anything is way off
if sys.argv[1] == '-tests':
passed_tests = True
for i in xrange(0, 1000):
passed_tests &= (i == saturate(dehydrate(i)))
print (passed_tests)
else:
user_input = sys.argv[2]
try:
if sys.argv[1] == '-s':
print (saturate(user_input))
elif sys.argv[1] == '-d':
print (dehydrate(int(user_input)))
else:
print ("I don't understand option %s" % sys.argv[1])
except ValueError as e:
print (e)

22
manage.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "shortio.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)

0
shortio/__init__.py Normal file
View File

124
shortio/settings.py Normal file
View File

@@ -0,0 +1,124 @@
"""
Django settings for shortio project.
Generated by 'django-admin startproject' using Django 1.11.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_wm51^)#98**^+k4cdrrzzoc@@slz7y(v((&)9^f7ko2_6t*!)'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'compressor',
'shorts',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'shortio.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'shortio.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/home/fergalm/dev/personal/shortio/static/'

7
shortio/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^shorts/', include('shorts.urls', namespace='urls')),
]

16
shortio/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for shortio project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "shortio.settings")
application = get_wsgi_application()

0
shorts/__init__.py Executable file
View File

5
shorts/admin.py Executable file
View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from shorts.models import Url
# Register your models here.
admin.site.register(Url)

45
shorts/api.py Executable file
View File

@@ -0,0 +1,45 @@
from rest_framework import generics, permissions
from .serializers import UrlSerializer
from .models import Url
from shorts.permissions import UrlAuthorCanEditPermission
"""
class UserList(generics.ListCreateAPIView):
model = User
serializer_class = UserSerializer
permissions_classess = [
permissions.AllowAny
]
class UserDetail(generics.RetrieveAPIView):
model = User
serializer_class = UserSerializer
lookup_field = 'username'
"""
class UrlMixin(object):
model = Url
serializer_class = UrlSerializer
permission_classes = [
UrlAuthorCanEditPermission
]
class UrlList(UrlMixin, generics.ListCreateAPIView):
pass
class UrlDetail(UrlMixin, generics.RetrieveUpdateDestroyAPIView):
pass
class UserUrlList(generics.ListAPIView):
model = Url
serializer_class = UrlSerializer
def get_queryset(self):
queryset = super(UserUrlList, self).get_queryset()
return queryset.filter(user__username=self.kwargs.get('username'))

26
shorts/models.py Executable file
View File

@@ -0,0 +1,26 @@
from django.conf import settings
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
from core import url
"""
class User(AbstractUser):
followers = models.ManyToManyField('self',
related_name='followees',
symmetrical=False)
"""
class Url(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_urls', null=True, blank=True)
url = models.CharField(max_length=2048)
shortened_url = models.CharField(max_length=6)
date_created = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.url
def shorten_url(self):
return "%s%s" % (settings.STATIC_URL, url.dehydrate(self.id))

19
shorts/permissions.py Executable file
View File

@@ -0,0 +1,19 @@
from rest_framework import permissions
class SafeMethodsOnlyPermission(permissions.BasePermission):
def has_permission(self, request, view):
return self.has_object_permission(request, view)
def has_object_permission(self, request, view, obj=None):
return request.method in permissions.SAFE_METHODS
class UrlAuthorCanEditPermission(SafeMethodsOnlyPermission):
def has_object_permission(self, request, view, obj=None):
if obj is None:
can_edit = True
else:
can_edit = request.user == obj.user
return can_edit or super(UrlAuthorCanEditPermission, self).has_object_permission(request, view, obj)

21
shorts/serializers.py Executable file
View File

@@ -0,0 +1,21 @@
from rest_framework import serializers
from .models import Url
"""
class UserSerializer(serializers.ModelSerializer):
urls = serializers.HyperlinkedIdentityField(
'user_urls',
lookup_field='username')
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'user_urls', )
"""
class UrlSerializer(serializers.ModelSerializer):
# user = UserSerializer(required=False)
shortened_url = serializers.Field(source='shorten_url')
class Meta:
model = Url

59
shorts/templates/base.html Executable file
View File

@@ -0,0 +1,59 @@
{% load staticfiles %}
{% load urls_extras %}
{% load compress %}
<html>
<head>
<title>ShortIO</title>
{% compress css %}
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/css/bootstrap.min.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/snippet.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/snippet.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/site.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/font-awesome.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/bootstrap-social.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/login.css" %}">
{% endcompress %}
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</head>
<body ng-app="shortioApp" style="background-image: url('{% get_random_image %}');">
<div ng-include="'{% static "js/app/views/header.html" %}'"></div>
<div class="bootshape login-form">
<div class="col-sm-6 col-md-5 col-lg-4 col-sm-offset-2 text-center login-popup-wrap">
<div class="login-popup">
<h1 class="title"><strong>ShortIO</strong></h1>
<aside>all your shorts are belong to us</aside>
</div>
<div ng-include="'{% static "js/app/views/url.html" %}'"></div>
</div>
</div>
{% compress js %}
<script src="{% static "js/underscore/underscore.js" %}"></script>
<script src="{% static "js/angular/angular.min.js" %}"></script>
<script src="{% static "js/angular/angular-resource.min.js" %}"></script>
<script src="{% static "js/angular/ui-bootstrap.js" %}"></script>
<script src="{% static "js/zepto.js" %}"></script>
<script src="{% static "js/script.js" %}"></script>
<script src="{% static "js/app/app.js" %}"></script>
<script src="{% static "js/app/directives/enter.js" %}"></script>
<script src="{% static "js/app/controllers/header.js" %}"></script>
<script src="{% static "js/app/controllers/url.js" %}"></script>
<script src="{% static "js/app/services/url.js" %}"></script>
{% endcompress %}
<script type="text/javascript">
angular.module('shortioApp')
.factory('AuthUser', function ($resource) {
return {
username: "{{ user.username|default:''|escapejs }}"
}
});
</script>
</body>
</html>

1
shorts/templates/detail.html Executable file
View File

@@ -0,0 +1 @@
{{ url }}

22
shorts/templates/index.html Executable file
View File

@@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'urls:create' %}" method="post">
{% csrf_token %}
<label for="url">Url: </label>
<input type="text" id="url" name="url" value="">
<input type="submit"/>
</form>
{% if error_message %}
<h5>{{ error_message }}</h5>
{% endif %}
{% if latest_url_list %}
<ul>
{% for url in latest_url_list %}
<li><a href="{% url 'urls:detail' url.id %}">{{ url.url }}</a></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1 @@
__author__ = 'fergalm'

View File

@@ -0,0 +1,9 @@
from django import template
from core.bing import get_iotd
register = template.Library()
@register.simple_tag
def get_random_image():
url = get_iotd()
return url

3
shorts/tests.py Executable file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

24
shorts/urls.py Executable file
View File

@@ -0,0 +1,24 @@
from django.conf.urls import include, url
from shorts import views
from .api import UrlList, UrlDetail, UserUrlList
# from .api import UserList, UserDetail
user_urls = [
url(r'^(?P<username>[0-9a-zA-Z_-]+)/urlss$', UserUrlList.as_view(), name='userurl-list'),
# url(r'^(?P<username>[0-9a-zA-Z_-]+)$', UserDetail.as_view(), name='user-detail'),
# url(r'^$', UserList.as_view(), name='user-list')
]
urls_urls = [
url(r'^(?P<pk>\d+)$', UrlDetail.as_view(), name='urls-detail'),
url(r'^$', UrlList.as_view(), name='urls-list')
]
urlpatterns = [
url(r'^users', include(user_urls)),
url(r'^urls', include(urls_urls)),
url(r'^$', views.index, name='index'),
url(r'^create', views.create, name='create'),
url(r'^(?P<url_id>\d+)/$', views.detail, name='detail')
]

26
shorts/views.py Executable file
View File

@@ -0,0 +1,26 @@
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from shorts.models import Url
# Create your views here.
def index(request, error=None):
latest_url_list = Url.objects.order_by('-date_created')[:5]
context = {'latest_url_list': latest_url_list, 'error_message': error}
return render(request, 'index.html', context)
def detail(request, url_id):
url = get_object_or_404(Url, pk=url_id)
return render(request, 'detail.html', {'url': url})
def create(request):
try:
u = Url(url=request.POST['url'])
u.save()
except Exception as ex:
return index(request, error=ex.message)
else:
return HttpResponseRedirect(reverse('urls:index'))

59
templates/base.html Executable file
View File

@@ -0,0 +1,59 @@
{% load staticfiles %}
{% load urls_extras %}
{% load compress %}
<html>
<head>
<title>ShortIO</title>
{% compress css %}
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/css/bootstrap.min.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/snippet.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "themes/wed/snippet.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/site.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/font-awesome.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/bootstrap-social.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "css/login.css" %}">
{% endcompress %}
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</head>
<body ng-app="shortioApp" style="background-image: url('{% get_random_image %}');">
<div ng-include="'{% static "js/app/views/header.html" %}'"></div>
<div class="bootshape login-form">
<div class="col-sm-6 col-md-5 col-lg-4 col-sm-offset-2 text-center login-popup-wrap">
<div class="login-popup">
<h1 class="title"><strong>ShortIO</strong></h1>
<aside>all your shorts are belong to us</aside>
</div>
<div ng-include="'{% static "js/app/views/url.html" %}'"></div>
</div>
</div>
{% compress js %}
<script src="{% static "js/underscore/underscore.js" %}"></script>
<script src="{% static "js/angular/angular.min.js" %}"></script>
<script src="{% static "js/angular/angular-resource.min.js" %}"></script>
<script src="{% static "js/angular/ui-bootstrap.js" %}"></script>
<script src="{% static "js/zepto.js" %}"></script>
<script src="{% static "js/script.js" %}"></script>
<script src="{% static "js/app/app.js" %}"></script>
<script src="{% static "js/app/directives/enter.js" %}"></script>
<script src="{% static "js/app/controllers/header.js" %}"></script>
<script src="{% static "js/app/controllers/url.js" %}"></script>
<script src="{% static "js/app/services/url.js" %}"></script>
{% endcompress %}
<script type="text/javascript">
angular.module('shortioApp')
.factory('AuthUser', function ($resource) {
return {
username: "{{ user.username|default:''|escapejs }}"
}
});
</script>
</body>
</html>

61
templates/dialogs/login.html Executable file
View File

@@ -0,0 +1,61 @@
{% load socialaccount %}
<div class="modal-header login_modal_header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h2 class="modal-title" id="myModalLabel">Login to Your Account</h2>
</div>
<div class="modal-body login-modal">
<p>Logging in allows you to save your shorts and view statistics</p>
<br/>
<div class="clearfix"></div>
<div id='social-icons-conatainer'>
<div class='modal-body-left'>
<div class="form-group">
<input type="text" id="username" placeholder="Enter your name" value=""
class="form-control login-field">
<i class="fa fa-user login-field-icon"></i>
</div>
<div class="form-group">
<input type="password" id="login-pass" placeholder="Password" value="" class="form-control login-field">
<i class="fa fa-lock login-field-icon"></i>
</div>
<a href="#" class="btn btn-success modal-login-btn">Login</a>
<a href="#" class="login-link text-center">Lost your password?</a>
</div>
<div class='modal-body-right'>
<div class="modal-social-icons">
<a href="{% provider_login_url "facebook" method="oauth2" %}"} class="btn btn-block btn-social btn-facebook">
<i class="fa fa-facebook"></i> Sign in with Facebook
</a>
<a href="{% provider_login_url "twitter" method="js_sdk" %}"} class="btn btn-block btn-social btn-twitter">
<i class="fa fa-twitter"></i> Sign in with Twitter
</a>
<a href="{% provider_login_url "google" method="js_sdk" %}"} class="btn btn-block btn-social btn-google-plus">
<i class="fa fa-google-plus"></i> Sign in with Google
</a>
<a href="{% provider_login_url "stackexchange" method="js_sdk" %}"} class="btn btn-block btn-social btn-stackexchange">
<i class="fa fa-stack-exchange"></i> Sign in with Stackexchange
</a>
<a href="{% provider_login_url "github" method="js_sdk" %}"} class="btn btn-block btn-social btn-github">
<i class="fa fa-github"></i> Sign in with Github
</a>
</div>
</div>
<div id='center-line'> OR</div>
</div>
<div class="clearfix"></div>
<div class="form-group modal-register-btn">
<button class="btn btn-default"> New User Please Register</button>
</div>
</div>
<div class="clearfix"></div>
<div class="modal-footer login_modal_footer">
</div>
{% comment %}
<a href="{% provider_login_url "facebook" method="js_sdk" %}">Facebook Connect</a>
{% endcomment %}