Removing taggit and other refactors
parent
faab5b2929
commit
814ccb2d54
|
@ -2,7 +2,7 @@
|
|||
from django.db import models
|
||||
|
||||
from greenmine.base.utils.slug import slugify_uniquely as slugify
|
||||
from greenmine.taggit.managers import TaggableManager
|
||||
from greenmine.base.fields import DictField
|
||||
|
||||
|
||||
class Document(models.Model):
|
||||
|
@ -18,8 +18,7 @@ class Document(models.Model):
|
|||
owner = models.ForeignKey('auth.User', related_name='documents')
|
||||
attached_file = models.FileField(upload_to="documents", max_length=1000,
|
||||
null=True, blank=True)
|
||||
|
||||
tags = TaggableManager()
|
||||
tags = DictField()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.db import models
|
||||
|
||||
from greenmine.base.utils.slug import slugify_uniquely
|
||||
from greenmine.taggit.managers import TaggableManager
|
||||
from greenmine.base.fields import DictField
|
||||
|
||||
|
||||
class Question(models.Model):
|
||||
|
@ -23,8 +24,7 @@ class Question(models.Model):
|
|||
watchers = models.ManyToManyField('auth.User',
|
||||
related_name='question_watch', null=True,
|
||||
blank=True)
|
||||
|
||||
tags = TaggableManager()
|
||||
tags = DictField()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
|
@ -41,3 +41,4 @@ class QuestionResponse(models.Model):
|
|||
|
||||
question = models.ForeignKey('Question', related_name='responses')
|
||||
owner = models.ForeignKey('auth.User', related_name='questions_responses')
|
||||
tags = DictField()
|
||||
|
|
|
@ -8,14 +8,33 @@ from greenmine.scrum.models import Project, Milestone, UserStory, Change, \
|
|||
ChangeAttachment, Task
|
||||
|
||||
|
||||
class MilestoneInline(admin.TabularInline):
|
||||
model = Milestone
|
||||
fields = ('name', 'owner', 'estimated_start', 'estimated_finish', 'closed', 'disponibility', 'order')
|
||||
sortable_field_name = 'order'
|
||||
extra = 0
|
||||
|
||||
class UserStoryInline(admin.TabularInline):
|
||||
model = UserStory
|
||||
fields = ('subject', 'order')
|
||||
sortable_field_name = 'order'
|
||||
extra = 0
|
||||
|
||||
def get_inline_instances(self, request, obj=None):
|
||||
if obj:
|
||||
return obj.user_stories.filter(mileston__isnone=True)
|
||||
else:
|
||||
return UserStory.objects.none()
|
||||
|
||||
class ProjectAdmin(reversion.VersionAdmin):
|
||||
list_display = ["name", "owner"]
|
||||
inlines = [MilestoneInline, UserStoryInline]
|
||||
|
||||
admin.site.register(Project, ProjectAdmin)
|
||||
|
||||
|
||||
class MilestoneAdmin(reversion.VersionAdmin):
|
||||
list_display = ["name", "project", "owner"]
|
||||
list_display = ["name", "project", "owner", "closed", "estimated_start", "estimated_finish"]
|
||||
|
||||
admin.site.register(Milestone, MilestoneAdmin)
|
||||
|
||||
|
|
|
@ -13,17 +13,10 @@ from django.contrib.auth.models import User
|
|||
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
||||
from greenmine.base.fields import DictField
|
||||
from greenmine.base.utils import iter_points
|
||||
from greenmine.taggit.managers import TaggableManager
|
||||
|
||||
from greenmine.scrum.choices import *
|
||||
from greenmine.scrum.utils import SCRUM_STATES
|
||||
|
||||
|
||||
class ProjectManager(models.Manager):
|
||||
def get_by_natural_key(self, slug):
|
||||
return self.get(slug=slug)
|
||||
|
||||
|
||||
class Project(models.Model):
|
||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||
name = models.CharField(max_length=250, unique=True)
|
||||
|
@ -31,7 +24,7 @@ class Project(models.Model):
|
|||
description = models.TextField(blank=False)
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True, auto_now=True)
|
||||
|
||||
owner = models.ForeignKey("auth.User", related_name="projects")
|
||||
public = models.BooleanField(default=True)
|
||||
|
@ -47,7 +40,7 @@ class Project(models.Model):
|
|||
show_sprint_burndown = models.BooleanField(default=False, blank=True)
|
||||
total_story_points = models.FloatField(default=None, null=True)
|
||||
|
||||
objects = ProjectManager()
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
|
@ -101,38 +94,16 @@ class Project(models.Model):
|
|||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def natural_key(self):
|
||||
return (self.slug,)
|
||||
|
||||
@property
|
||||
def unasociated_user_stories(self):
|
||||
return self.user_stories.filter(milestone__isnull=True)
|
||||
|
||||
@property
|
||||
def all_participants(self):
|
||||
return User.objects.filter(group__in=self.groups)
|
||||
|
||||
@property
|
||||
def default_milestone(self):
|
||||
return self.milestones.order_by('-created_date')[0]
|
||||
|
||||
def __repr__(self):
|
||||
return u"<Project %s>" % (self.slug)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify_uniquely(self.name, self.__class__)
|
||||
else:
|
||||
self.modified_date = timezone.now()
|
||||
|
||||
super(Project, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class MilestoneManager(models.Manager):
|
||||
def get_by_natural_key(self, name, project):
|
||||
return self.get(name=name, project__slug=project)
|
||||
|
||||
|
||||
class Milestone(models.Model):
|
||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||
name = models.CharField(max_length=200, db_index=True)
|
||||
|
@ -143,11 +114,13 @@ class Milestone(models.Model):
|
|||
estimated_finish = models.DateField(null=True, default=None)
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True, auto_now=True)
|
||||
closed = models.BooleanField(default=False)
|
||||
|
||||
disponibility = models.FloatField(null=True, default=0.0)
|
||||
objects = MilestoneManager()
|
||||
order = models.PositiveSmallIntegerField("Order")
|
||||
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_date']
|
||||
|
@ -162,58 +135,12 @@ class Milestone(models.Model):
|
|||
total = sum(iter_points(self.user_stories.all()))
|
||||
return "{0:.1f}".format(total)
|
||||
|
||||
def get_points_done_at_date(self, date):
|
||||
"""
|
||||
Get completed story points for this milestone before the date.
|
||||
"""
|
||||
total = 0.0
|
||||
|
||||
for item in self.user_stories.filter(status__in=SCRUM_STATES.get_finished_us_states()):
|
||||
if item.tasks.filter(finished_date__lt=date).count() > 0:
|
||||
if item.points == -1:
|
||||
continue
|
||||
|
||||
if item.points == -2:
|
||||
total += 0.5
|
||||
continue
|
||||
|
||||
total += item.points
|
||||
|
||||
return "{0:.1f}".format(total)
|
||||
|
||||
@property
|
||||
def completed_points(self):
|
||||
"""
|
||||
Get a total of completed points.
|
||||
"""
|
||||
|
||||
queryset = self.user_stories.filter(status__in=SCRUM_STATES.get_finished_us_states())
|
||||
total = sum(iter_points(queryset))
|
||||
return "{0:.1f}".format(total)
|
||||
|
||||
@property
|
||||
def percentage_completed(self):
|
||||
return "{0:.1f}".format(
|
||||
(float(self.completed_points) * 100) / float(self.total_points)
|
||||
)
|
||||
|
||||
def natural_key(self):
|
||||
return (self.name,) + self.project.natural_key()
|
||||
|
||||
natural_key.dependencies = ['greenmine.Project']
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return u"<Milestone %s>" % (self.id)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.id:
|
||||
self.modified_date = timezone.now()
|
||||
|
||||
super(Milestone, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class UserStory(models.Model):
|
||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||
|
@ -230,10 +157,8 @@ class UserStory(models.Model):
|
|||
choices=SCRUM_STATES.get_us_choices(),
|
||||
db_index=True, default="open")
|
||||
|
||||
tags = TaggableManager()
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True)
|
||||
modified_date = models.DateTimeField(auto_now_add=True, auto_now=True)
|
||||
tested = models.BooleanField(default=False)
|
||||
|
||||
subject = models.CharField(max_length=500)
|
||||
|
@ -247,6 +172,8 @@ class UserStory(models.Model):
|
|||
team_requirement = models.BooleanField(default=False)
|
||||
order = models.PositiveSmallIntegerField("Order")
|
||||
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['order']
|
||||
unique_together = ('ref', 'project')
|
||||
|
@ -258,46 +185,11 @@ class UserStory(models.Model):
|
|||
return u"{0} ({1})".format(self.subject, self.ref)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.id:
|
||||
self.modified_date = timezone.now()
|
||||
if not self.ref:
|
||||
self.ref = ref_uniquely(self.project, self.__class__)
|
||||
|
||||
super(UserStory, self).save(*args, **kwargs)
|
||||
|
||||
""" Propertys """
|
||||
def update_status(self):
|
||||
tasks = self.tasks.all()
|
||||
used_states = []
|
||||
|
||||
for task in tasks:
|
||||
used_states.append(task.fake_status)
|
||||
used_states = set(used_states)
|
||||
|
||||
for state in SCRUM_STATES.ordered_us_states():
|
||||
for task_state in used_states:
|
||||
if task_state == state:
|
||||
self.status = state
|
||||
self.save()
|
||||
return None
|
||||
return None
|
||||
|
||||
@property
|
||||
def tasks_new(self):
|
||||
return self.tasks.filter(status__in=SCRUM_STATES.get_task_states_for_us_state('open'))
|
||||
|
||||
@property
|
||||
def tasks_progress(self):
|
||||
return self.tasks.filter(status__in=SCRUM_STATES.get_task_states_for_us_state('progress'))
|
||||
|
||||
@property
|
||||
def tasks_completed(self):
|
||||
return self.tasks.filter(status__in=SCRUM_STATES.get_task_states_for_us_state('completed'))
|
||||
|
||||
@property
|
||||
def tasks_closed(self):
|
||||
return self.tasks.filter(status__in=SCRUM_STATES.get_task_states_for_us_state('closed'))
|
||||
|
||||
|
||||
class Change(models.Model):
|
||||
change_type = models.IntegerField(choices=TASK_CHANGE_CHOICES)
|
||||
|
@ -310,7 +202,8 @@ class Change(models.Model):
|
|||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
data = DictField()
|
||||
data = DictField(blank=True, null=True)
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
|
||||
class ChangeAttachment(models.Model):
|
||||
|
@ -320,6 +213,7 @@ class ChangeAttachment(models.Model):
|
|||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
attached_file = models.FileField(upload_to="files/msg", max_length=500,
|
||||
null=True, blank=True)
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
|
||||
class TaskQuerySet(models.query.QuerySet):
|
||||
|
@ -445,8 +339,7 @@ class Task(models.Model):
|
|||
null=True)
|
||||
|
||||
changes = generic.GenericRelation(Change)
|
||||
|
||||
tags = TaggableManager()
|
||||
tags = DictField(blank=True, null=True)
|
||||
|
||||
objects = TaskManager()
|
||||
|
||||
|
|
|
@ -205,7 +205,6 @@ INSTALLED_APPS = [
|
|||
'greenmine.scrum',
|
||||
'greenmine.wiki',
|
||||
'greenmine.documents',
|
||||
'greenmine.taggit',
|
||||
'greenmine.questions',
|
||||
'greenmine.search',
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
VERSION = (0, 9, 3)
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Tag, TaggedItem
|
||||
|
||||
|
||||
class TaggedItemInline(admin.StackedInline):
|
||||
model = TaggedItem
|
||||
|
||||
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
list_display = ["name"]
|
||||
inlines = [
|
||||
TaggedItemInline
|
||||
]
|
||||
|
||||
|
||||
admin.site.register(Tag, TagAdmin)
|
|
@ -1,22 +0,0 @@
|
|||
# myapp/api.py
|
||||
from tastypie.resources import ModelResource
|
||||
from tastypie.authentication import SessionAuthentication
|
||||
from tastypie.authorization import DjangoAuthorization
|
||||
|
||||
from greenmine.taggit.models import Tag, TaggedItem
|
||||
|
||||
|
||||
class TagResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = Tag.objects.all()
|
||||
resource_name = 'tag'
|
||||
authentication = SessionAuthentication()
|
||||
authorization = DjangoAuthorization()
|
||||
|
||||
|
||||
class TaggedItemResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = TaggedItem.objects.all()
|
||||
resource_name = 'taggeditem'
|
||||
authentication = SessionAuthentication()
|
||||
authorization = DjangoAuthorization()
|
|
@ -1,67 +0,0 @@
|
|||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-taggit\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-09-07 09:26-0700\n"
|
||||
"PO-Revision-Date: 2010-09-07 09:26-0700\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: forms.py:20
|
||||
msgid "Please provide a comma-separated list of tags."
|
||||
msgstr "Bitte eine durch Komma getrennte Schlagwortliste eingeben."
|
||||
|
||||
#: managers.py:39 managers.py:83 models.py:50
|
||||
msgid "Tags"
|
||||
msgstr "Schlagwörter"
|
||||
|
||||
#: managers.py:84
|
||||
msgid "A comma-separated list of tags."
|
||||
msgstr "Eine durch Komma getrennte Schlagwortliste."
|
||||
|
||||
#: models.py:10
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: models.py:11
|
||||
msgid "Slug"
|
||||
msgstr "Kürzel"
|
||||
|
||||
#: models.py:49
|
||||
msgid "Tag"
|
||||
msgstr "Schlagwort"
|
||||
|
||||
#: models.py:56
|
||||
#, python-format
|
||||
msgid "%(object)s tagged with %(tag)s"
|
||||
msgstr "%(object)s verschlagwortet mit %(tag)s"
|
||||
|
||||
#: models.py:100
|
||||
msgid "Object id"
|
||||
msgstr "Objekt-ID"
|
||||
|
||||
#: models.py:104 models.py:110
|
||||
msgid "Content type"
|
||||
msgstr "Inhaltstyp"
|
||||
|
||||
#: models.py:138
|
||||
msgid "Tagged Item"
|
||||
msgstr "Verschlagwortetes Objekt"
|
||||
|
||||
#: models.py:139
|
||||
msgid "Tagged Items"
|
||||
msgstr "Verschlagwortete Objekte"
|
||||
|
||||
#: contrib/suggest/models.py:57
|
||||
msgid ""
|
||||
"Enter a valid Regular Expression. To make it case-insensitive include \"(?i)"
|
||||
"\" in your expression."
|
||||
msgstr ""
|
||||
"Bitte einen regulären Ausdruck eingeben. Fügen Sie \"(?i) \" dem "
|
||||
"Ausdruck hinzu, um nicht zwischen Groß- und Kleinschreibung zu "
|
||||
"unterscheiden."
|
|
@ -1,68 +0,0 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-09-07 09:45-0700\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: forms.py:20
|
||||
msgid "Please provide a comma-separated list of tags."
|
||||
msgstr ""
|
||||
|
||||
#: managers.py:39 managers.py:83 models.py:50
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: managers.py:84
|
||||
msgid "A comma-separated list of tags."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:10
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:11
|
||||
msgid "Slug"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:49
|
||||
msgid "Tag"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:56
|
||||
#, python-format
|
||||
msgid "%(object)s tagged with %(tag)s"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:100
|
||||
msgid "Object id"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:104 models.py:110
|
||||
msgid "Content type"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:138
|
||||
msgid "Tagged Item"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:139
|
||||
msgid "Tagged Items"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/suggest/models.py:57
|
||||
msgid ""
|
||||
"Enter a valid Regular Expression. To make it case-insensitive include \"(?i)"
|
||||
"\" in your expression."
|
||||
msgstr ""
|
|
@ -1,69 +0,0 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django Taggit\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-06-26 12:47-0500\n"
|
||||
"PO-Revision-Date: 2010-06-26 12:54-0600\n"
|
||||
"Last-Translator: Alex <alex.gaynor@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: forms.py:20
|
||||
msgid "Please provide a comma-separated list of tags."
|
||||
msgstr "נא לספק רשימה של תגים מופרדת עם פסיקים."
|
||||
|
||||
#: managers.py:41
|
||||
#: managers.py:113
|
||||
#: models.py:18
|
||||
msgid "Tags"
|
||||
msgstr "תגיות"
|
||||
|
||||
#: managers.py:114
|
||||
msgid "A comma-separated list of tags."
|
||||
msgstr "רשימה של תגים מופרדת עם פסיקים."
|
||||
|
||||
#: models.py:10
|
||||
msgid "Name"
|
||||
msgstr "שם"
|
||||
|
||||
#: models.py:11
|
||||
msgid "Slug"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:17
|
||||
msgid "Tag"
|
||||
msgstr "תג"
|
||||
|
||||
#: models.py:56
|
||||
#, python-format
|
||||
msgid "%(object)s tagged with %(tag)s"
|
||||
msgstr "%(object)s מתויג עם %(tag)s"
|
||||
|
||||
#: models.py:86
|
||||
msgid "Object id"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:87
|
||||
msgid "Content type"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:92
|
||||
msgid "Tagged Item"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:93
|
||||
msgid "Tagged Items"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/suggest/models.py:57
|
||||
msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
|
||||
msgstr ""
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-taggit\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-09-07 09:45-0700\n"
|
||||
"PO-Revision-Date: 2010-09-07 23:04+0100\n"
|
||||
"Last-Translator: Jeffrey Gelens <jeffrey@gelens.org>\n"
|
||||
"Language-Team: Dutch\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: forms.py:20
|
||||
msgid "Please provide a comma-separated list of tags."
|
||||
msgstr "Geef een door komma gescheiden lijst van tags."
|
||||
|
||||
#: managers.py:39
|
||||
#: managers.py:83
|
||||
#: models.py:50
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
|
||||
#: managers.py:84
|
||||
msgid "A comma-separated list of tags."
|
||||
msgstr "Een door komma gescheiden lijst van tags."
|
||||
|
||||
#: models.py:10
|
||||
msgid "Name"
|
||||
msgstr "Naam"
|
||||
|
||||
#: models.py:11
|
||||
msgid "Slug"
|
||||
msgstr "Slug"
|
||||
|
||||
#: models.py:49
|
||||
msgid "Tag"
|
||||
msgstr "Tag"
|
||||
|
||||
#: models.py:56
|
||||
#, python-format
|
||||
msgid "%(object)s tagged with %(tag)s"
|
||||
msgstr "%(object)s getagged met %(tag)s"
|
||||
|
||||
#: models.py:100
|
||||
msgid "Object id"
|
||||
msgstr "Object-id"
|
||||
|
||||
#: models.py:104
|
||||
#: models.py:110
|
||||
msgid "Content type"
|
||||
msgstr "Inhoudstype"
|
||||
|
||||
#: models.py:138
|
||||
msgid "Tagged Item"
|
||||
msgstr "Object getagged"
|
||||
|
||||
#: models.py:139
|
||||
msgid "Tagged Items"
|
||||
msgstr "Objecten getagged"
|
||||
|
||||
#: contrib/suggest/models.py:57
|
||||
msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
|
||||
msgstr "Voer een valide reguliere expressie in. Voeg \"(?i)\" aan de expressie toe om deze hoofdletter ongevoelig te maken."
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django Taggit\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2010-06-11 11:28+0700\n"
|
||||
"PO-Revision-Date: 2010-06-11 11:30+0700\n"
|
||||
"Last-Translator: Igor 'idle sign' Starikov <idlesign@yandex.ru>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
|
||||
#: forms.py:20
|
||||
msgid "Please provide a comma-separated list of tags."
|
||||
msgstr "Укажите метки через запятую."
|
||||
|
||||
#: managers.py:41
|
||||
#: managers.py:101
|
||||
#: models.py:17
|
||||
msgid "Tags"
|
||||
msgstr "Метки"
|
||||
|
||||
#: managers.py:102
|
||||
msgid "A comma-separated list of tags."
|
||||
msgstr "Список меток через запятую."
|
||||
|
||||
#: models.py:9
|
||||
msgid "Name"
|
||||
msgstr "Название"
|
||||
|
||||
#: models.py:10
|
||||
msgid "Slug"
|
||||
msgstr "Слаг"
|
||||
|
||||
#: models.py:16
|
||||
msgid "Tag"
|
||||
msgstr "Метка"
|
||||
|
||||
#: models.py:55
|
||||
#, python-format
|
||||
msgid "%(object)s tagged with %(tag)s"
|
||||
msgstr "элемент «%(object)s» с меткой «%(tag)s»"
|
||||
|
||||
#: models.py:82
|
||||
msgid "Object id"
|
||||
msgstr "ID объекта"
|
||||
|
||||
#: models.py:83
|
||||
msgid "Content type"
|
||||
msgstr "Тип содержимого"
|
||||
|
||||
#: models.py:87
|
||||
msgid "Tagged Item"
|
||||
msgstr "Элемент с меткой"
|
||||
|
||||
#: models.py:88
|
||||
msgid "Tagged Items"
|
||||
msgstr "Элементы с меткой"
|
||||
|
||||
#: contrib/suggest/models.py:57
|
||||
msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
|
||||
msgstr "Введите регулярное выражение. Чтобы сделать его чувствительным к регистру укажите \"(?i)\"."
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.contrib.contenttypes.generic import GenericRelation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.db.models.fields.related import ManyToManyRel, RelatedField, add_lazy_relation
|
||||
from django.db.models.related import RelatedObject
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from greenmine.taggit.models import TaggedItem, GenericTaggedItemBase
|
||||
from greenmine.taggit.utils import require_instance_manager
|
||||
|
||||
|
||||
class TaggableRel(ManyToManyRel):
|
||||
def __init__(self):
|
||||
self.related_name = None
|
||||
self.limit_choices_to = {}
|
||||
self.symmetrical = True
|
||||
self.multiple = True
|
||||
self.through = None
|
||||
|
||||
|
||||
class TaggableManager(RelatedField):
|
||||
def __init__(self, verbose_name=_("Tags"),
|
||||
help_text=_("A comma-separated list of tags."), through=None,
|
||||
blank=False):
|
||||
self.through = through or TaggedItem
|
||||
self.rel = TaggableRel()
|
||||
self.verbose_name = verbose_name
|
||||
self.help_text = help_text
|
||||
self.blank = blank
|
||||
self.editable = True
|
||||
self.unique = False
|
||||
self.creates_table = False
|
||||
self.db_column = None
|
||||
self.choices = None
|
||||
self.serialize = False
|
||||
self.null = True
|
||||
self.creation_counter = models.Field.creation_counter
|
||||
models.Field.creation_counter += 1
|
||||
|
||||
def __get__(self, instance, model):
|
||||
if instance is not None and instance.pk is None:
|
||||
raise ValueError("%s objects need to have a primary key value "
|
||||
"before you can access their tags." % model.__name__)
|
||||
manager = _TaggableManager(
|
||||
through=self.through, model=model, instance=instance
|
||||
)
|
||||
return manager
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
self.name = self.column = name
|
||||
self.model = cls
|
||||
cls._meta.add_field(self)
|
||||
setattr(cls, name, self)
|
||||
if not cls._meta.abstract:
|
||||
if isinstance(self.through, basestring):
|
||||
def resolve_related_class(field, model, cls):
|
||||
self.through = model
|
||||
self.post_through_setup(cls)
|
||||
add_lazy_relation(
|
||||
cls, self, self.through, resolve_related_class
|
||||
)
|
||||
else:
|
||||
self.post_through_setup(cls)
|
||||
|
||||
def post_through_setup(self, cls):
|
||||
self.use_gfk = (
|
||||
self.through is None or issubclass(self.through, GenericTaggedItemBase)
|
||||
)
|
||||
self.rel.to = self.through._meta.get_field("tag").rel.to
|
||||
if self.use_gfk:
|
||||
tagged_items = GenericRelation(self.through)
|
||||
tagged_items.contribute_to_class(cls, "tagged_items")
|
||||
|
||||
def save_form_data(self, instance, value):
|
||||
getattr(instance, self.name).set(*value)
|
||||
|
||||
def value_from_object(self, instance):
|
||||
if instance.pk:
|
||||
return self.through.objects.filter(**self.through.lookup_kwargs(instance))
|
||||
return self.through.objects.none()
|
||||
|
||||
def related_query_name(self):
|
||||
return self.model._meta.module_name
|
||||
|
||||
def m2m_reverse_name(self):
|
||||
return self.through._meta.get_field_by_name("tag")[0].column
|
||||
|
||||
def m2m_target_field_name(self):
|
||||
return self.model._meta.pk.name
|
||||
|
||||
def m2m_reverse_target_field_name(self):
|
||||
return self.rel.to._meta.pk.name
|
||||
|
||||
def m2m_column_name(self):
|
||||
if self.use_gfk:
|
||||
return self.through._meta.virtual_fields[0].fk_field
|
||||
return self.through._meta.get_field('content_object').column
|
||||
|
||||
def db_type(self, connection=None):
|
||||
return None
|
||||
|
||||
def m2m_db_table(self):
|
||||
return self.through._meta.db_table
|
||||
|
||||
def extra_filters(self, pieces, pos, negate):
|
||||
if negate or not self.use_gfk:
|
||||
return []
|
||||
prefix = "__".join(["tagged_items"] + pieces[:pos-2])
|
||||
cts = map(ContentType.objects.get_for_model, _get_subclasses(self.model))
|
||||
if len(cts) == 1:
|
||||
return [("%s__content_type" % prefix, cts[0])]
|
||||
return [("%s__content_type__in" % prefix, cts)]
|
||||
|
||||
def bulk_related_objects(self, new_objs, using):
|
||||
return []
|
||||
|
||||
|
||||
class _TaggableManager(models.Manager):
|
||||
def __init__(self, through, model, instance):
|
||||
self.through = through
|
||||
self.model = model
|
||||
self.instance = instance
|
||||
|
||||
def get_query_set(self):
|
||||
return self.through.tags_for(self.model, self.instance)
|
||||
|
||||
def _lookup_kwargs(self):
|
||||
return self.through.lookup_kwargs(self.instance)
|
||||
|
||||
@require_instance_manager
|
||||
def add(self, *tags):
|
||||
str_tags = set([
|
||||
t
|
||||
for t in tags
|
||||
if not isinstance(t, self.through.tag_model())
|
||||
])
|
||||
tag_objs = set(tags) - str_tags
|
||||
# If str_tags has 0 elements Django actually optimizes that to not do a
|
||||
# query. Malcolm is very smart.
|
||||
existing = self.through.tag_model().objects.filter(
|
||||
name__in=str_tags
|
||||
)
|
||||
tag_objs.update(existing)
|
||||
|
||||
for new_tag in str_tags - set(t.name for t in existing):
|
||||
tag_objs.add(self.through.tag_model().objects.create(name=new_tag))
|
||||
|
||||
for tag in tag_objs:
|
||||
self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs())
|
||||
|
||||
@require_instance_manager
|
||||
def set(self, *tags):
|
||||
self.clear()
|
||||
self.add(*tags)
|
||||
|
||||
@require_instance_manager
|
||||
def remove(self, *tags):
|
||||
self.through.objects.filter(**self._lookup_kwargs()).filter(
|
||||
tag__name__in=tags).delete()
|
||||
|
||||
@require_instance_manager
|
||||
def clear(self):
|
||||
self.through.objects.filter(**self._lookup_kwargs()).delete()
|
||||
|
||||
def most_common(self):
|
||||
return self.get_query_set().annotate(
|
||||
num_times=models.Count(self.through.tag_relname())
|
||||
).order_by('-num_times')
|
||||
|
||||
@require_instance_manager
|
||||
def similar_objects(self):
|
||||
lookup_kwargs = self._lookup_kwargs()
|
||||
lookup_keys = sorted(lookup_kwargs)
|
||||
qs = self.through.objects.values(*lookup_kwargs.keys())
|
||||
qs = qs.annotate(n=models.Count('pk'))
|
||||
qs = qs.exclude(**lookup_kwargs)
|
||||
qs = qs.filter(tag__in=self.all())
|
||||
qs = qs.order_by('-n')
|
||||
|
||||
# TODO: This all feels like a bit of a hack.
|
||||
items = {}
|
||||
if len(lookup_keys) == 1:
|
||||
# Can we do this without a second query by using a select_related()
|
||||
# somehow?
|
||||
f = self.through._meta.get_field_by_name(lookup_keys[0])[0]
|
||||
objs = f.rel.to._default_manager.filter(**{
|
||||
"%s__in" % f.rel.field_name: [r["content_object"] for r in qs]
|
||||
})
|
||||
for obj in objs:
|
||||
items[(getattr(obj, f.rel.field_name),)] = obj
|
||||
else:
|
||||
preload = {}
|
||||
for result in qs:
|
||||
preload.setdefault(result['content_type'], set())
|
||||
preload[result["content_type"]].add(result["object_id"])
|
||||
|
||||
for ct, obj_ids in preload.iteritems():
|
||||
ct = ContentType.objects.get_for_id(ct)
|
||||
for obj in ct.model_class()._default_manager.filter(pk__in=obj_ids):
|
||||
items[(ct.pk, obj.pk)] = obj
|
||||
|
||||
results = []
|
||||
for result in qs:
|
||||
obj = items[
|
||||
tuple(result[k] for k in lookup_keys)
|
||||
]
|
||||
obj.similar_tags = result["n"]
|
||||
results.append(obj)
|
||||
return results
|
||||
|
||||
|
||||
def _get_subclasses(model):
|
||||
subclasses = [model]
|
||||
for f in model._meta.get_all_field_names():
|
||||
field = model._meta.get_field_by_name(f)[0]
|
||||
if (isinstance(field, RelatedObject) and getattr(field.field.rel, "parent_link", None)):
|
||||
subclasses.extend(_get_subclasses(field.model))
|
||||
return subclasses
|
|
@ -1,249 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
import django
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.generic import GenericForeignKey
|
||||
from django.db import connection, models, IntegrityError, transaction
|
||||
from django.template.defaultfilters import slugify as default_slugify
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
|
||||
class TagBase(models.Model):
|
||||
name = models.CharField(verbose_name=_('Name'), max_length=100)
|
||||
slug = models.SlugField(verbose_name=_('Slug'), unique=True, max_length=100)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.pk and not self.slug:
|
||||
self.slug = self.slugify(self.name)
|
||||
if django.VERSION >= (1, 2):
|
||||
from django.db import router
|
||||
using = kwargs.get("using") or router.db_for_write(
|
||||
type(self), instance=self)
|
||||
# Make sure we write to the same db for all attempted writes,
|
||||
# with a multi-master setup, theoretically we could try to
|
||||
# write and rollback on different DBs
|
||||
kwargs["using"] = using
|
||||
trans_kwargs = {"using": using}
|
||||
else:
|
||||
trans_kwargs = {}
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
try:
|
||||
sid = transaction.savepoint(**trans_kwargs)
|
||||
res = super(TagBase, self).save(*args, **kwargs)
|
||||
transaction.savepoint_commit(sid, **trans_kwargs)
|
||||
return res
|
||||
except IntegrityError:
|
||||
transaction.savepoint_rollback(sid, **trans_kwargs)
|
||||
self.slug = self.slugify(self.name, i)
|
||||
else:
|
||||
return super(TagBase, self).save(*args, **kwargs)
|
||||
|
||||
def slugify(self, tag, i=None):
|
||||
slug = default_slugify(tag)
|
||||
if i is not None:
|
||||
slug += "_%d" % i
|
||||
return slug
|
||||
|
||||
|
||||
class TagManager(models.Manager):
|
||||
def tags_for_queryset(self, queryset, counts=True, min_count=None):
|
||||
"""
|
||||
Obtain a list of tags associated with instances of a model
|
||||
contained in the given queryset.
|
||||
|
||||
If ``counts`` is True, a ``count`` attribute will be added to
|
||||
each tag, indicating how many times it has been used against
|
||||
the Model class in question.
|
||||
|
||||
If ``min_count`` is given, only tags which have a ``count``
|
||||
greater than or equal to ``min_count`` will be returned.
|
||||
Passing a value for ``min_count`` implies ``counts=True``.
|
||||
"""
|
||||
|
||||
compiler = queryset.query.get_compiler(using='default')
|
||||
extra_joins = ' '.join(compiler.get_from_clause()[0][1:])
|
||||
where, params = queryset.query.where.as_sql(
|
||||
compiler.quote_name_unless_alias, compiler.connection
|
||||
)
|
||||
|
||||
if where:
|
||||
extra_criteria = 'AND %s' % where
|
||||
else:
|
||||
extra_criteria = ''
|
||||
return self._get_usage(queryset.model, counts, min_count, extra_joins, extra_criteria, params)
|
||||
|
||||
def _get_usage(self, model, counts=False, min_count=None, extra_joins=None, extra_criteria=None, params=None):
|
||||
"""
|
||||
Perform the custom SQL query for ``usage_for_model`` and
|
||||
``usage_for_queryset``.
|
||||
"""
|
||||
if min_count is not None:
|
||||
counts = True
|
||||
|
||||
model_table = qn(model._meta.db_table)
|
||||
model_pk = '%s.%s' % (model_table, qn(model._meta.pk.column))
|
||||
|
||||
query = """
|
||||
SELECT DISTINCT %(tag)s.id, %(tag)s.name%(count_sql)s
|
||||
FROM
|
||||
%(tag)s
|
||||
INNER JOIN %(tagged_item_alias)s
|
||||
ON %(tag)s.id = %(tagged_item)s.tag_id
|
||||
INNER JOIN %(model)s
|
||||
ON %(tagged_item)s.object_id = %(model_pk)s
|
||||
%%s
|
||||
WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
|
||||
%%s
|
||||
GROUP BY %(tag)s.id, %(tag)s.name
|
||||
%%s
|
||||
ORDER BY %(tag)s.name ASC""" % {
|
||||
'tag': qn(Tag._meta.db_table),
|
||||
'count_sql': counts and (', COUNT(%s)' % model_pk) or '',
|
||||
'tagged_item_alias': qn(TaggedItem._meta.db_table) + " tagged_item_alias",
|
||||
'tagged_item': "tagged_item_alias",
|
||||
'model': model_table,
|
||||
'model_pk': model_pk,
|
||||
'content_type_id': ContentType.objects.get_for_model(model).pk,
|
||||
}
|
||||
|
||||
min_count_sql = ''
|
||||
if min_count is not None:
|
||||
min_count_sql = 'HAVING COUNT(%s) >= %%s' % model_pk
|
||||
params.append(min_count)
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.execute(query % (extra_joins, extra_criteria, min_count_sql), params)
|
||||
tags = []
|
||||
for row in cursor.fetchall():
|
||||
t = Tag(*row[:2])
|
||||
if counts:
|
||||
t.count = row[2]
|
||||
tags.append(t)
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
class Tag(TagBase):
|
||||
objects = TagManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Tag")
|
||||
verbose_name_plural = _("Tags")
|
||||
|
||||
def to_dict(self):
|
||||
self_dict = {
|
||||
'id': self.pk,
|
||||
'name': self.name,
|
||||
}
|
||||
|
||||
return self_dict
|
||||
|
||||
|
||||
class ItemBase(models.Model):
|
||||
def __unicode__(self):
|
||||
return ugettext("%(object)s tagged with %(tag)s") % {
|
||||
"object": self.content_object,
|
||||
"tag": self.tag
|
||||
}
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def tag_model(cls):
|
||||
return cls._meta.get_field_by_name("tag")[0].rel.to
|
||||
|
||||
@classmethod
|
||||
def tag_relname(cls):
|
||||
return cls._meta.get_field_by_name('tag')[0].rel.related_name
|
||||
|
||||
@classmethod
|
||||
def lookup_kwargs(cls, instance):
|
||||
return {
|
||||
'content_object': instance
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def bulk_lookup_kwargs(cls, instances):
|
||||
return {
|
||||
"content_object__in": instances,
|
||||
}
|
||||
|
||||
|
||||
class TaggedItemBase(ItemBase):
|
||||
tag = models.ForeignKey(Tag, related_name="%(app_label)s_%(class)s_items")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def tags_for(cls, model, instance=None):
|
||||
if instance is not None:
|
||||
return cls.tag_model().objects.filter(**{
|
||||
'%s__content_object' % cls.tag_relname(): instance
|
||||
})
|
||||
return cls.tag_model().objects.filter(**{
|
||||
'%s__content_object__isnull' % cls.tag_relname(): False
|
||||
}).distinct()
|
||||
|
||||
|
||||
class GenericTaggedItemBase(ItemBase):
|
||||
object_id = models.IntegerField(verbose_name=_('Object id'), db_index=True)
|
||||
if django.VERSION < (1, 2):
|
||||
content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
verbose_name=_('Content type'),
|
||||
related_name="%(class)s_tagged_items"
|
||||
)
|
||||
else:
|
||||
content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
verbose_name=_('Content type'),
|
||||
related_name="%(app_label)s_%(class)s_tagged_items"
|
||||
)
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def lookup_kwargs(cls, instance):
|
||||
return {
|
||||
'object_id': instance.pk,
|
||||
'content_type': ContentType.objects.get_for_model(instance)
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def bulk_lookup_kwargs(cls, instances):
|
||||
# TODO: instances[0], can we assume there are instances.
|
||||
return {
|
||||
"object_id__in": [instance.pk for instance in instances],
|
||||
"content_type": ContentType.objects.get_for_model(instances[0]),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def tags_for(cls, model, instance=None):
|
||||
ct = ContentType.objects.get_for_model(model)
|
||||
kwargs = {
|
||||
"%s__content_type" % cls.tag_relname(): ct
|
||||
}
|
||||
if instance is not None:
|
||||
kwargs["%s__object_id" % cls.tag_relname()] = instance.pk
|
||||
return cls.tag_model().objects.filter(**kwargs).distinct()
|
||||
|
||||
|
||||
class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
|
||||
class Meta:
|
||||
verbose_name = _("Tagged Item")
|
||||
verbose_name_plural = _("Tagged Items")
|
|
@ -1,139 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.utils.functional import wraps
|
||||
from django.db.models import Count
|
||||
|
||||
|
||||
def get_tags_for_queryset(queryset, tags_attribute='tags'):
|
||||
"""
|
||||
Given a queryset and a taggint atributte returns a list with the form
|
||||
[{'count': number_of_tagged_items, 'tags_attribute': 'id_of the tag'}]
|
||||
[{'count': 3, 'tags': 1}, {'count': 2, 'tags': 2}, {'count': 1, 'tags': 3}]
|
||||
"""
|
||||
return queryset.values(tags_attribute).annotate(count=Count(tags_attribute)).order_by('-count')
|
||||
|
||||
|
||||
def parse_tags(tagstring):
|
||||
"""
|
||||
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.
|
||||
|
||||
Ported from Jonathan Buchanan's `django-tagging
|
||||
<http://django-tagging.googlecode.com/>`_
|
||||
"""
|
||||
if not tagstring:
|
||||
return []
|
||||
|
||||
tagstring = force_unicode(tagstring)
|
||||
|
||||
# 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 tagstring and u'"' not in tagstring:
|
||||
words = list(set(split_strip(tagstring, 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(tagstring)
|
||||
try:
|
||||
while True:
|
||||
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(string, delimiter=u','):
|
||||
"""
|
||||
Splits ``string`` on ``delimiter``, stripping each resulting string
|
||||
and returning a list of non-empty strings.
|
||||
|
||||
Ported from Jonathan Buchanan's `django-tagging
|
||||
<http://django-tagging.googlecode.com/>`_
|
||||
"""
|
||||
if not string:
|
||||
return []
|
||||
|
||||
words = [w.strip() for w in string.split(delimiter)]
|
||||
return [w for w in words if w]
|
||||
|
||||
|
||||
def edit_string_for_tags(tags):
|
||||
"""
|
||||
Given list of ``Tag`` instances, creates a string representation of
|
||||
the list suitable for editing by the user, such that submitting the
|
||||
given string representation back without changing it will give the
|
||||
same list of tags.
|
||||
|
||||
Tag names which contain commas will be double quoted.
|
||||
|
||||
If any tag name which isn't being quoted contains whitespace, the
|
||||
resulting string of tag names will be comma-delimited, otherwise
|
||||
it will be space-delimited.
|
||||
|
||||
Ported from Jonathan Buchanan's `django-tagging
|
||||
<http://django-tagging.googlecode.com/>`_
|
||||
"""
|
||||
names = []
|
||||
for tag in tags:
|
||||
name = tag.name
|
||||
if u',' in name or u' ' in name:
|
||||
names.append('"%s"' % name)
|
||||
else:
|
||||
names.append(name)
|
||||
return u', '.join(sorted(names))
|
||||
|
||||
|
||||
def require_instance_manager(func):
|
||||
@wraps(func)
|
||||
def inner(self, *args, **kwargs):
|
||||
if self.instance is None:
|
||||
raise TypeError("Can't call %s with a non-instance manager" % func.__name__)
|
||||
return func(self, *args, **kwargs)
|
||||
return inner
|
|
@ -11,7 +11,6 @@ from scrum.api import *
|
|||
from questions.api import *
|
||||
from documents.api import *
|
||||
from profile.api import *
|
||||
from taggit.api import *
|
||||
from wiki.api import *
|
||||
|
||||
v1_api = Api(api_name='gm')
|
||||
|
@ -25,10 +24,7 @@ v1_api.register(QuestionResource())
|
|||
v1_api.register(QuestionResponseResource())
|
||||
v1_api.register(DocumentResource())
|
||||
v1_api.register(ProfileResource())
|
||||
v1_api.register(TagResource())
|
||||
v1_api.register(TaggedItemResource())
|
||||
v1_api.register(WikiPageResource())
|
||||
v1_api.register(WikiPageHistoryResource())
|
||||
v1_api.register(WikiPageAttachmentResource())
|
||||
|
||||
urlpatterns = patterns('',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
|
||||
from greenmine.wiki.models import WikiPage, WikiPageHistory, WikiPageAttachment
|
||||
from greenmine.wiki.models import WikiPage, WikiPageAttachment
|
||||
|
||||
|
||||
class WikiPageAdmin(admin.ModelAdmin):
|
||||
|
@ -10,12 +10,6 @@ class WikiPageAdmin(admin.ModelAdmin):
|
|||
admin.site.register(WikiPage, WikiPageAdmin)
|
||||
|
||||
|
||||
class WikiPageHistoryAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "wikipage", "owner", "created_date"]
|
||||
|
||||
admin.site.register(WikiPageHistory, WikiPageHistoryAdmin)
|
||||
|
||||
|
||||
class WikiPageAttachmentAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "wikipage", "owner"]
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from tastypie.resources import ModelResource
|
|||
from tastypie.authentication import SessionAuthentication
|
||||
from tastypie.authorization import DjangoAuthorization
|
||||
|
||||
from greenmine.wiki.models import WikiPage, WikiPageHistory, WikiPageAttachment
|
||||
from greenmine.wiki.models import WikiPage, WikiPageAttachment
|
||||
|
||||
|
||||
class WikiPageResource(ModelResource):
|
||||
|
@ -14,14 +14,6 @@ class WikiPageResource(ModelResource):
|
|||
authorization = DjangoAuthorization()
|
||||
|
||||
|
||||
class WikiPageHistoryResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = WikiPageHistory.objects.all()
|
||||
resource_name = 'wikipagehistory'
|
||||
authentication = SessionAuthentication()
|
||||
authorization = DjangoAuthorization()
|
||||
|
||||
|
||||
class WikiPageAttachmentResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = WikiPageAttachment.objects.all()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.db import models
|
||||
|
||||
from greenmine.base.fields import DictField
|
||||
|
||||
|
||||
class WikiPage(models.Model):
|
||||
project = models.ForeignKey('scrum.Project', related_name='wiki_pages')
|
||||
|
@ -12,13 +14,7 @@ class WikiPage(models.Model):
|
|||
null=True)
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class WikiPageHistory(models.Model):
|
||||
wikipage = models.ForeignKey("WikiPage", related_name="history_entries")
|
||||
content = models.TextField(blank=True, null=True)
|
||||
created_date = models.DateTimeField()
|
||||
owner = models.ForeignKey("auth.User", related_name="wiki_page_historys")
|
||||
tags = DictField()
|
||||
|
||||
|
||||
class WikiPageAttachment(models.Model):
|
||||
|
@ -28,3 +24,4 @@ class WikiPageAttachment(models.Model):
|
|||
modified_date = models.DateTimeField(auto_now_add=True)
|
||||
attached_file = models.FileField(upload_to="files/wiki", max_length=500,
|
||||
null=True, blank=True)
|
||||
tags = DictField()
|
||||
|
|
Loading…
Reference in New Issue