Removing taggit and other refactors
parent
faab5b2929
commit
814ccb2d54
|
@ -2,7 +2,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely as slugify
|
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):
|
class Document(models.Model):
|
||||||
|
@ -18,8 +18,7 @@ class Document(models.Model):
|
||||||
owner = models.ForeignKey('auth.User', related_name='documents')
|
owner = models.ForeignKey('auth.User', related_name='documents')
|
||||||
attached_file = models.FileField(upload_to="documents", max_length=1000,
|
attached_file = models.FileField(upload_to="documents", max_length=1000,
|
||||||
null=True, blank=True)
|
null=True, blank=True)
|
||||||
|
tags = DictField()
|
||||||
tags = TaggableManager()
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely
|
from greenmine.base.utils.slug import slugify_uniquely
|
||||||
from greenmine.taggit.managers import TaggableManager
|
from greenmine.base.fields import DictField
|
||||||
|
|
||||||
|
|
||||||
class Question(models.Model):
|
class Question(models.Model):
|
||||||
|
@ -23,8 +24,7 @@ class Question(models.Model):
|
||||||
watchers = models.ManyToManyField('auth.User',
|
watchers = models.ManyToManyField('auth.User',
|
||||||
related_name='question_watch', null=True,
|
related_name='question_watch', null=True,
|
||||||
blank=True)
|
blank=True)
|
||||||
|
tags = DictField()
|
||||||
tags = TaggableManager()
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
|
@ -41,3 +41,4 @@ class QuestionResponse(models.Model):
|
||||||
|
|
||||||
question = models.ForeignKey('Question', related_name='responses')
|
question = models.ForeignKey('Question', related_name='responses')
|
||||||
owner = models.ForeignKey('auth.User', related_name='questions_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
|
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):
|
class ProjectAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["name", "owner"]
|
list_display = ["name", "owner"]
|
||||||
|
inlines = [MilestoneInline, UserStoryInline]
|
||||||
|
|
||||||
admin.site.register(Project, ProjectAdmin)
|
admin.site.register(Project, ProjectAdmin)
|
||||||
|
|
||||||
|
|
||||||
class MilestoneAdmin(reversion.VersionAdmin):
|
class MilestoneAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["name", "project", "owner"]
|
list_display = ["name", "project", "owner", "closed", "estimated_start", "estimated_finish"]
|
||||||
|
|
||||||
admin.site.register(Milestone, MilestoneAdmin)
|
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.utils.slug import slugify_uniquely, ref_uniquely
|
||||||
from greenmine.base.fields import DictField
|
from greenmine.base.fields import DictField
|
||||||
from greenmine.base.utils import iter_points
|
from greenmine.base.utils import iter_points
|
||||||
from greenmine.taggit.managers import TaggableManager
|
|
||||||
|
|
||||||
from greenmine.scrum.choices import *
|
from greenmine.scrum.choices import *
|
||||||
from greenmine.scrum.utils import SCRUM_STATES
|
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):
|
class Project(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
name = models.CharField(max_length=250, unique=True)
|
name = models.CharField(max_length=250, unique=True)
|
||||||
|
@ -31,7 +24,7 @@ class Project(models.Model):
|
||||||
description = models.TextField(blank=False)
|
description = models.TextField(blank=False)
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
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")
|
owner = models.ForeignKey("auth.User", related_name="projects")
|
||||||
public = models.BooleanField(default=True)
|
public = models.BooleanField(default=True)
|
||||||
|
@ -47,7 +40,7 @@ class Project(models.Model):
|
||||||
show_sprint_burndown = models.BooleanField(default=False, blank=True)
|
show_sprint_burndown = models.BooleanField(default=False, blank=True)
|
||||||
total_story_points = models.FloatField(default=None, null=True)
|
total_story_points = models.FloatField(default=None, null=True)
|
||||||
|
|
||||||
objects = ProjectManager()
|
tags = DictField(blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
|
@ -101,38 +94,16 @@ class Project(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
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):
|
def __repr__(self):
|
||||||
return u"<Project %s>" % (self.slug)
|
return u"<Project %s>" % (self.slug)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = slugify_uniquely(self.name, self.__class__)
|
self.slug = slugify_uniquely(self.name, self.__class__)
|
||||||
else:
|
|
||||||
self.modified_date = timezone.now()
|
|
||||||
|
|
||||||
super(Project, self).save(*args, **kwargs)
|
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):
|
class Milestone(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
name = models.CharField(max_length=200, db_index=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)
|
estimated_finish = models.DateField(null=True, default=None)
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
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)
|
closed = models.BooleanField(default=False)
|
||||||
|
|
||||||
disponibility = models.FloatField(null=True, default=0.0)
|
disponibility = models.FloatField(null=True, default=0.0)
|
||||||
objects = MilestoneManager()
|
order = models.PositiveSmallIntegerField("Order")
|
||||||
|
|
||||||
|
tags = DictField(blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-created_date']
|
ordering = ['-created_date']
|
||||||
|
@ -162,58 +135,12 @@ class Milestone(models.Model):
|
||||||
total = sum(iter_points(self.user_stories.all()))
|
total = sum(iter_points(self.user_stories.all()))
|
||||||
return "{0:.1f}".format(total)
|
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):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return u"<Milestone %s>" % (self.id)
|
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):
|
class UserStory(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
|
@ -230,10 +157,8 @@ class UserStory(models.Model):
|
||||||
choices=SCRUM_STATES.get_us_choices(),
|
choices=SCRUM_STATES.get_us_choices(),
|
||||||
db_index=True, default="open")
|
db_index=True, default="open")
|
||||||
|
|
||||||
tags = TaggableManager()
|
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
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)
|
tested = models.BooleanField(default=False)
|
||||||
|
|
||||||
subject = models.CharField(max_length=500)
|
subject = models.CharField(max_length=500)
|
||||||
|
@ -247,6 +172,8 @@ class UserStory(models.Model):
|
||||||
team_requirement = models.BooleanField(default=False)
|
team_requirement = models.BooleanField(default=False)
|
||||||
order = models.PositiveSmallIntegerField("Order")
|
order = models.PositiveSmallIntegerField("Order")
|
||||||
|
|
||||||
|
tags = DictField(blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
unique_together = ('ref', 'project')
|
unique_together = ('ref', 'project')
|
||||||
|
@ -258,46 +185,11 @@ class UserStory(models.Model):
|
||||||
return u"{0} ({1})".format(self.subject, self.ref)
|
return u"{0} ({1})".format(self.subject, self.ref)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.id:
|
|
||||||
self.modified_date = timezone.now()
|
|
||||||
if not self.ref:
|
if not self.ref:
|
||||||
self.ref = ref_uniquely(self.project, self.__class__)
|
self.ref = ref_uniquely(self.project, self.__class__)
|
||||||
|
|
||||||
super(UserStory, self).save(*args, **kwargs)
|
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):
|
class Change(models.Model):
|
||||||
change_type = models.IntegerField(choices=TASK_CHANGE_CHOICES)
|
change_type = models.IntegerField(choices=TASK_CHANGE_CHOICES)
|
||||||
|
@ -310,7 +202,8 @@ class Change(models.Model):
|
||||||
object_id = models.PositiveIntegerField()
|
object_id = models.PositiveIntegerField()
|
||||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
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):
|
class ChangeAttachment(models.Model):
|
||||||
|
@ -320,6 +213,7 @@ class ChangeAttachment(models.Model):
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
attached_file = models.FileField(upload_to="files/msg", max_length=500,
|
attached_file = models.FileField(upload_to="files/msg", max_length=500,
|
||||||
null=True, blank=True)
|
null=True, blank=True)
|
||||||
|
tags = DictField(blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class TaskQuerySet(models.query.QuerySet):
|
class TaskQuerySet(models.query.QuerySet):
|
||||||
|
@ -445,8 +339,7 @@ class Task(models.Model):
|
||||||
null=True)
|
null=True)
|
||||||
|
|
||||||
changes = generic.GenericRelation(Change)
|
changes = generic.GenericRelation(Change)
|
||||||
|
tags = DictField(blank=True, null=True)
|
||||||
tags = TaggableManager()
|
|
||||||
|
|
||||||
objects = TaskManager()
|
objects = TaskManager()
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,6 @@ INSTALLED_APPS = [
|
||||||
'greenmine.scrum',
|
'greenmine.scrum',
|
||||||
'greenmine.wiki',
|
'greenmine.wiki',
|
||||||
'greenmine.documents',
|
'greenmine.documents',
|
||||||
'greenmine.taggit',
|
|
||||||
'greenmine.questions',
|
'greenmine.questions',
|
||||||
'greenmine.search',
|
'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 questions.api import *
|
||||||
from documents.api import *
|
from documents.api import *
|
||||||
from profile.api import *
|
from profile.api import *
|
||||||
from taggit.api import *
|
|
||||||
from wiki.api import *
|
from wiki.api import *
|
||||||
|
|
||||||
v1_api = Api(api_name='gm')
|
v1_api = Api(api_name='gm')
|
||||||
|
@ -25,10 +24,7 @@ v1_api.register(QuestionResource())
|
||||||
v1_api.register(QuestionResponseResource())
|
v1_api.register(QuestionResponseResource())
|
||||||
v1_api.register(DocumentResource())
|
v1_api.register(DocumentResource())
|
||||||
v1_api.register(ProfileResource())
|
v1_api.register(ProfileResource())
|
||||||
v1_api.register(TagResource())
|
|
||||||
v1_api.register(TaggedItemResource())
|
|
||||||
v1_api.register(WikiPageResource())
|
v1_api.register(WikiPageResource())
|
||||||
v1_api.register(WikiPageHistoryResource())
|
|
||||||
v1_api.register(WikiPageAttachmentResource())
|
v1_api.register(WikiPageAttachmentResource())
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from greenmine.wiki.models import WikiPage, WikiPageHistory, WikiPageAttachment
|
from greenmine.wiki.models import WikiPage, WikiPageAttachment
|
||||||
|
|
||||||
|
|
||||||
class WikiPageAdmin(admin.ModelAdmin):
|
class WikiPageAdmin(admin.ModelAdmin):
|
||||||
|
@ -10,12 +10,6 @@ class WikiPageAdmin(admin.ModelAdmin):
|
||||||
admin.site.register(WikiPage, WikiPageAdmin)
|
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):
|
class WikiPageAttachmentAdmin(admin.ModelAdmin):
|
||||||
list_display = ["id", "wikipage", "owner"]
|
list_display = ["id", "wikipage", "owner"]
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from tastypie.resources import ModelResource
|
||||||
from tastypie.authentication import SessionAuthentication
|
from tastypie.authentication import SessionAuthentication
|
||||||
from tastypie.authorization import DjangoAuthorization
|
from tastypie.authorization import DjangoAuthorization
|
||||||
|
|
||||||
from greenmine.wiki.models import WikiPage, WikiPageHistory, WikiPageAttachment
|
from greenmine.wiki.models import WikiPage, WikiPageAttachment
|
||||||
|
|
||||||
|
|
||||||
class WikiPageResource(ModelResource):
|
class WikiPageResource(ModelResource):
|
||||||
|
@ -14,14 +14,6 @@ class WikiPageResource(ModelResource):
|
||||||
authorization = DjangoAuthorization()
|
authorization = DjangoAuthorization()
|
||||||
|
|
||||||
|
|
||||||
class WikiPageHistoryResource(ModelResource):
|
|
||||||
class Meta:
|
|
||||||
queryset = WikiPageHistory.objects.all()
|
|
||||||
resource_name = 'wikipagehistory'
|
|
||||||
authentication = SessionAuthentication()
|
|
||||||
authorization = DjangoAuthorization()
|
|
||||||
|
|
||||||
|
|
||||||
class WikiPageAttachmentResource(ModelResource):
|
class WikiPageAttachmentResource(ModelResource):
|
||||||
class Meta:
|
class Meta:
|
||||||
queryset = WikiPageAttachment.objects.all()
|
queryset = WikiPageAttachment.objects.all()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from greenmine.base.fields import DictField
|
||||||
|
|
||||||
|
|
||||||
class WikiPage(models.Model):
|
class WikiPage(models.Model):
|
||||||
project = models.ForeignKey('scrum.Project', related_name='wiki_pages')
|
project = models.ForeignKey('scrum.Project', related_name='wiki_pages')
|
||||||
|
@ -12,13 +14,7 @@ class WikiPage(models.Model):
|
||||||
null=True)
|
null=True)
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
tags = DictField()
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
class WikiPageAttachment(models.Model):
|
class WikiPageAttachment(models.Model):
|
||||||
|
@ -28,3 +24,4 @@ class WikiPageAttachment(models.Model):
|
||||||
modified_date = models.DateTimeField(auto_now_add=True)
|
modified_date = models.DateTimeField(auto_now_add=True)
|
||||||
attached_file = models.FileField(upload_to="files/wiki", max_length=500,
|
attached_file = models.FileField(upload_to="files/wiki", max_length=500,
|
||||||
null=True, blank=True)
|
null=True, blank=True)
|
||||||
|
tags = DictField()
|
||||||
|
|
Loading…
Reference in New Issue