Removing taggit and other refactors

remotes/origin/enhancement/email-actions
Jesús Espino 2013-03-22 22:21:40 +01:00
parent faab5b2929
commit 814ccb2d54
20 changed files with 45 additions and 1145 deletions

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -205,7 +205,6 @@ INSTALLED_APPS = [
'greenmine.scrum',
'greenmine.wiki',
'greenmine.documents',
'greenmine.taggit',
'greenmine.questions',
'greenmine.search',

View File

@ -1 +0,0 @@
VERSION = (0, 9, 3)

View File

@ -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)

View File

@ -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()

View File

@ -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."

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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."

View File

@ -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)\"."

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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('',

View File

@ -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"]

View File

@ -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()

View File

@ -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()