Commits

ken cochrane committed b6cba40

initial checkin version 0.1

Comments (0)

Files changed (12)

+syntax: glob
+*.pyc
+dist
+MANIFEST
+_build
+_static
+.DS_Store
+Copyright (c) 2011, Ken Cochrane
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--------------
+Django-Shorty
+--------------
+Django shorty makes it real easy to create short urls from longer urls in your application. Works for both external and internal URLS
+
+------------
+Requirements
+------------
+Django 1.1 or higher
+Django South 0.7 or higher
+
+
+------
+Setup
+------
+
+Add shorty to your installed apps in your settings.py
+
+Add the shorty.urls to your projects urls.py
+
+Add the following variables to your settings.py
+
+
+SHORT_URL_DOMAIN (required)
+------------------
+This is the URL for your short url service. No Trailing slash
+
+# SHORT_URL_DOMAIN = "http://example.com/s"
+
+
+SHORTY_UNIQUE_CODE (required)
+------------------
+This a unique code that you would use to make it harder for someone to guess your short URL sequence,
+it randomized the code values that are returned.
+
+##############################################################################################
+# This is how you can generate a random key for the SHORTY_UNIQUE_CODE.
+#
+# base62_digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+# l = list(base62_digits)
+# import random
+# random.seed(54) # replace 54 with a random number
+# random.shuffle(l)
+# s = ''.join(l)
+# print s
+#
+###############################################################################################
+
+# SHORTY_UNIQUE_CODE = 'eK1BPRYNwCVjhcfuZ4OSIFtzGiL63mry790dXgDaH5bEnoWqQ2AJMkvplTsx8U'
+
+
+SHORTY_PRE_SEED (optional)
+---------------
+DON'T CHANGE ONCE YOU HAVE STARTED USING, OR ELSE YOU COULD END UP WITH DUPLICATES!
+
+If you want your encoded value to always start with a value use this.
+
+# SHORTY_PRE_SEED = "A"
+# all encoded values would look like this. http://example.com/s/A{encoded_value}
+
+
+SHORTY_POST_SEED (optional)
+----------------
+DON'T CHANGE ONCE YOU HAVE STARTED USING, OR ELSE YOU COULD END UP WITH DUPLICATES!
+
+If you want your encoded value to always end with a value use this.
+
+# SHORTY_POST_SEED = "Z"
+# all encoded values would look like this. http://example.com/s/{encoded_value}Z
+
+
+SHORTY_HOME_URL_REDIRECT (required)
+------------------------
+Redirect to the website defined in SHORTY_HOME_URL_REDIRECT. 
+This is setup so that you can have a website that just does redirects
+and another site with all content. This will redirect home page to
+the other site.
+
+# SHORTY_HOME_URL_REDIRECT = "http://example.com"
+
+
+-----------
+How to Use
+-----------
+
+Anywhere you have a long url you can call the get_shorty method and it will return a short version of it.
+
+    from shorty import get_shorty
+
+    # the long url needs to be a fully qualified URL, no relative urls.
+    my_long_url = "http://kencochrane.net/blog/2011/06/django-gunicorn-nginx-supervisord-fabric-centos55/"
+
+    short_url = get_shorty(my_long_url)
+
+    print short_url
+
+
+or if you have a model you can add a property to that model and get a short url real easy.
+
+
+    @property
+    def short_url(self):
+        """ If your model has an absolute_url method that returns the absolute_url for itself
+            then you can use that to get the url and then call get_shorty """
+            
+        from shorty import get_shorty
+        return get_shorty(self.absolute_url)
+        
+    @property
+    def short_url2(self):
+        """ Use the reverse utility method to generate a url 
+            and then make it short and return it """
+            
+        from django.core.urlresolvers import reverse
+        from shorty import get_shorty
+        
+        url = reverse('view_post', args=[self.pk])
+        return get_shorty(self.absolute_url)
+
+
+
+- Add caching
+- If SHORT_URL_DOMAIN isn't declared try and guess what it should be (maybe using django sites record?)
+- Add better documentation
+- Add unit tests
+from distutils.core import setup
+
+setup(
+    name='django-shorty',
+    version='0.1',
+    description='Django Short url application, to make it easy to make short urls',
+    maintainer='Ken Cochrane',
+    maintainer_email='KenCochrane@gmail.com',
+    url='https://bitbucket.org/kencochrane/django-shorty/',
+    classifiers=[
+        'Programming Language :: Python',
+        'Framework :: Django',
+    ],
+    packages=['shorty',],
+    long_description=open('README').read(),
+)
+

shorty/__init__.py

+from shorty.models import ShortLink
+
+def get_shorty(long_url):
+    """ Get Shorty links """
+    short_url = ShortLink.objects.get_or_create_link(long_url)
+    return short_url.get_absolute_url()

shorty/migrations/0001_initial.py

+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+    
+    def forwards(self, orm):
+        
+        # Adding model 'ShortLink'
+        db.create_table('shorty_shortlink', (
+            ('code', self.gf('django.db.models.fields.CharField')(unique=True, max_length=62, db_index=True)),
+            ('create_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)),
+            ('link_url', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)),
+            ('num_visits', self.gf('django.db.models.fields.SmallIntegerField')(default=0)),
+            ('last_update', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+        ))
+        db.send_create_signal('shorty', ['ShortLink'])
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting model 'ShortLink'
+        db.delete_table('shorty_shortlink')
+    
+    
+    models = {
+        'shorty.shortlink': {
+            'Meta': {'object_name': 'ShortLink'},
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '62', 'db_index': 'True'}),
+            'create_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'link_url': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+            'num_visits': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'})
+        }
+    }
+    
+    complete_apps = ['shorty']
Add a comment to this file

shorty/migrations/__init__.py

Empty file added.

+from django.db import models
+from django.conf import settings
+
+from shorty.shorty_utils import encoder, temp_code
+
+# Get the settings from settings.py
+SHORT_URL_DOMAIN = getattr(settings, 'SHORT_URL_DOMAIN', None) # required
+
+#------------------------------------------------------------------------------
+class ShortLinkManager(models.Manager):
+    """ Short Link Manager """
+
+    def get_or_create_link(self, long_url):
+        """ get or create link """
+        try:
+            return self.get(link_url=long_url)
+        except self.model.DoesNotExist:
+            # add a temp code right now, then replace with real code in second
+            # code is based on the model id, since we don't have yet we need to
+            # use the temp code.
+            model = self.model(link_url=long_url, code=temp_code())
+            model.save()
+            # replace temp code with real code.
+            code = encoder(str(model.id))
+            model.code = code
+            model.save()
+            return model
+
+#------------------------------------------------------------------------------
+class ShortLink(models.Model):
+    """ ShortLink """
+    code = models.CharField(max_length=62, db_index=True, unique=True)
+    link_url = models.CharField(max_length=255, db_index=True, unique=True)
+    create_date = models.DateTimeField(auto_now_add=True)
+    last_update = models.DateTimeField(auto_now=True)
+    num_visits = models.SmallIntegerField(default=0)
+    
+    # add the custom manager
+    objects = ShortLinkManager()
+
+    #---------------------------------------------------------------------------
+    def get_absolute_url(self):
+        """ Abosulte URL """
+        if SHORT_URL_DOMAIN:
+            return "%s/%s" % (SHORT_URL_DOMAIN, self.code)
+        else:
+            raise NameError("SHORT_URL_DOMAIN is not defined \
+                             in the settings.py file")
+        
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        """ Unicode to string method """
+        return u"[%s] - %s ; %s" % (self.pk, self.code, self.link_url)
+
+    

shorty/shorty_utils.py

+from datetime import datetime
+import time
+from django.conf import settings
+
+# Get the settings from settings.py
+SHORTY_UNIQUE_CODE = getattr(settings, 'SHORTY_UNIQUE_CODE', None) # required
+SHORTY_PRE_SEED = getattr(settings, 'SHORTY_PRE_SEED', None) # optional
+SHORTY_POST_SEED = getattr(settings, 'SHORTY_POST_SEED', None) # optional
+
+def temp_code():
+    """We need a unique temp code, so we get a timestamp to the 
+       microsecond and that should be good enough"""
+       
+    return "%s%s" % (time.mktime(datetime.now().timetuple()), 
+                     datetime.now().microsecond
+                    )
+
+def encoder(value):
+    """ Encode the value """
+    # Based on http://code.activestate.com/recipes/111286/
+    
+    fromdigits='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    # todigits is our unique key that we generate to make it a little harder to
+    # guess our links, it makes them more random
+    if SHORTY_UNIQUE_CODE:
+        todigits = SHORTY_UNIQUE_CODE
+    else:
+        raise NameError("SHORTY_UNIQUE_CODE is not defined \
+                         in the settings.py file")
+    
+    # if you want to put a seed for the code add it here, 
+    # there is a PRE_SEED and POST_SEED, that will go before and 
+    # after the code before encoding.
+    # DON'T CHANGE ONCE STARTED USING.
+    digits = "%s%s%s" % (SHORTY_PRE_SEED, value, SHORTY_POST_SEED)
+    
+    # make an integer out of the number
+    x_val = 0
+    for digit in str(digits):
+        x_val = x_val * len(fromdigits) + fromdigits.index(digit)
+
+    # create the result in base 'len(todigits)'
+    if x_val == 0:
+        res = todigits[0]
+    else:
+        res = ""
+        while x_val > 0:
+            digit = x_val % len(todigits)
+            res = todigits[digit] + res
+            x_val = int(x_val / len(todigits))
+    return res
+from django.conf.urls.defaults import patterns, url
+
+from shorty import views
+
+urlpatterns = patterns('',
+    url(r'^(?P<code>\w+)$', views.redirect, name='redirect'),
+    url(r'^$', views.home, name='home'),
+)
+from django.http import HttpResponsePermanentRedirect
+from django.shortcuts import get_object_or_404
+from shorty.models import ShortLink
+from django.conf import settings
+
+# Get the settings from settings.py
+SHORTY_HOME_URL_REDIRECT = getattr(settings, 'SHORTY_HOME_URL_REDIRECT', None)
+
+def redirect(request, code):
+    """
+    Redirect to a given object from a short URL.
+    """
+    
+    # Try to look up the object. If it's not a valid object, bail. with 404
+    short_link = get_object_or_404(ShortLink, code=code)
+    short_link.num_visits += 1
+    short_link.save()
+    
+    return HttpResponsePermanentRedirect(short_link.link_url)
+    
+def home(request):
+    """
+    Redirect to the website defined in SHORTY_HOME_URL_REDIRECT. 
+    This is setup so that you can have a website that just does redirects
+    and another site with all content. This will redirect home page to
+    the other site.
+    """
+    
+    if SHORTY_HOME_URL_REDIRECT:
+        return HttpResponsePermanentRedirect(SHORTY_HOME_URL_REDIRECT)
+    else:
+        raise NameError("SHORTY_HOME_URL_REDIRECT is \
+                         not defined in the settings.py file")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.