diff --git a/migrate.py b/migrate.py index b5c47a3..a388529 100644 --- a/migrate.py +++ b/migrate.py @@ -1,5 +1,7 @@ #!/usr/bin/python +from django.utils.encoding import force_unicode + import MySQLdb from django.contrib.auth.models import User @@ -8,7 +10,7 @@ from snipts.models import Comment, Snipt from taggit.models import Tag, TaggedItem from taggit.utils import parse_tags -conn = MySQLdb.connect(host='localhost', user='root', passwd='root', db='sniptold') +conn = MySQLdb.connect(host='localhost', user='root', passwd='', db='sniptold') cursor = conn.cursor() def i(): @@ -99,7 +101,7 @@ def snipts(): public=public, created=created, ) - for t in parse_tags(tags): + for t in parse_tag_input(tags): snipt.tags.add(t) snipt.save() @@ -133,3 +135,84 @@ def comments(): print "Couldn't get snipt " + str(snipt_id) print 'Done with comments' + +def parse_tag_input(input): + """ + Parses tag input, with multiple word input being activated and + delineated by commas and double quotes. Quotes take precedence, so + they may contain commas. + + Returns a sorted list of unique tag names. + """ + if not input: + return [] + + input = force_unicode(input) + + # Special case - if there are no commas or double quotes in the + # input, we don't *do* a recall... I mean, we know we only need to + # split on spaces. + if u',' not in input and u'"' not in input: + words = list(set(split_strip(input, u' '))) + words.sort() + return words + + words = [] + buffer = [] + # Defer splitting of non-quoted sections until we know if there are + # any unquoted commas. + to_be_split = [] + saw_loose_comma = False + open_quote = False + i = iter(input) + try: + while 1: + c = i.next() + if c == u'"': + if buffer: + to_be_split.append(u''.join(buffer)) + buffer = [] + # Find the matching quote + open_quote = True + c = i.next() + while c != u'"': + buffer.append(c) + c = i.next() + if buffer: + word = u''.join(buffer).strip() + if word: + words.append(word) + buffer = [] + open_quote = False + else: + if not saw_loose_comma and c == u',': + saw_loose_comma = True + buffer.append(c) + except StopIteration: + # If we were parsing an open quote which was never closed treat + # the buffer as unquoted. + if buffer: + if open_quote and u',' in buffer: + saw_loose_comma = True + to_be_split.append(u''.join(buffer)) + if to_be_split: + if saw_loose_comma: + delimiter = u',' + else: + delimiter = u' ' + for chunk in to_be_split: + words.extend(split_strip(chunk, delimiter)) + words = list(set(words)) + words.sort() + return words + +def split_strip(input, delimiter=u','): + """ + Splits ``input`` on ``delimiter``, stripping each resulting string + and returning a list of non-empty strings. + """ + if not input: + return [] + + words = [w.strip() for w in input.split(delimiter)] + return [w for w in words if w] diff --git a/requirements.txt b/requirements.txt index 9c7d573..ee2e88a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,6 +20,9 @@ BeautifulSoup django-taggit django-postmark johnny-cache +lxml python-memcached +pyyaml South +uuid Werkzeug diff --git a/settings.py b/settings.py index bd7fe88..831a16d 100644 --- a/settings.py +++ b/settings.py @@ -58,11 +58,11 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.redirects', - 'api', 'compressor', 'django_bcrypt', 'south', 'taggit', + 'tastypie', 'snipts', ) diff --git a/snipts/admin.py b/snipts/admin.py index 54ac756..91e9546 100644 --- a/snipts/admin.py +++ b/snipts/admin.py @@ -9,8 +9,7 @@ class CommentInline(admin.TabularInline): allow_add = False class SniptAdmin(admin.ModelAdmin): - # TODO: Make user readonly - #readonly_fields = ('user',) + readonly_fields = ('user',) list_display = ('title', 'slug', 'user', 'lexer', 'public', 'created', 'modified',) search_fields = ('title', 'user__username', 'tags', 'lexer', 'id',) ordering = ('created',) diff --git a/snipts/api.py b/snipts/api.py new file mode 100644 index 0000000..e35e9f7 --- /dev/null +++ b/snipts/api.py @@ -0,0 +1,8 @@ +from tastypie.resources import ModelResource +from snipts.models import Snipt + + +class SniptResource(ModelResource): + class Meta: + queryset = Snipt.objects.all() + resource_name = 'snipt' diff --git a/snipts/models.py b/snipts/models.py index 0e197b3..e0f56cd 100644 --- a/snipts/models.py +++ b/snipts/models.py @@ -1,15 +1,16 @@ +from django.template.defaultfilters import slugify from django.contrib.auth.models import User from django.db import models from taggit.managers import TaggableManager class Snipt(models.Model): - """An individual code snippet.""" + """An individual Snipt.""" user = models.ForeignKey(User) title = models.CharField(max_length=255) - slug = models.SlugField() + slug = models.SlugField(blank=True) tags = TaggableManager() lexer = models.CharField(max_length=50) @@ -23,8 +24,14 @@ class Snipt(models.Model): created = models.DateTimeField(auto_now_add=False, editable=False) modified = models.DateTimeField(auto_now=True, editable=False) + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.title)[:50] + + return super(Entry, self).save(*args, **kwargs) + def __unicode__(self): - return u'%s' %(self.title) + return self.title class Comment(models.Model): """A comment on a Snipt""" @@ -37,3 +44,6 @@ class Comment(models.Model): # TODO Set auto_now_add back to True for production! created = models.DateTimeField(auto_now_add=False, editable=False) modified = models.DateTimeField(auto_now=True, editable=False) + + def __unicode__(self): + return u'%s on %s' %(self.user, self.snipt) diff --git a/urls.py b/urls.py index a2b4797..ad8361f 100644 --- a/urls.py +++ b/urls.py @@ -6,6 +6,10 @@ admin.autodiscover() from views import home +from snipts.api import SniptResource + +snipt_resource = SniptResource() + urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), @@ -14,7 +18,7 @@ urlpatterns = patterns('', url(r'^404/$', direct_to_template, {'template': '404.html'}), url(r'^500/$', direct_to_template, {'template': '500.html'}), - url(r'^api/', include('api.urls')), + url(r'^api/', include(snipt_resource.urls)), url(r'^$', home), )