From d3eda0a5cfeb15acdbdb4c7b30cd2e7861919c77 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 30 Mar 2013 14:57:32 +0100 Subject: [PATCH 1/9] Minor fixes on ref handling. --- greenmine/scrum/models.py | 23 +++++++++++++++++------ greenmine/scrum/serializers.py | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index 0bf7eb40..93094906 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -273,12 +273,6 @@ class UserStory(models.Model): def is_closed(self): return self.status.is_closed - def save(self, *args, **kwargs): - if self.ref is None and self.project: - self.ref = ref_uniquely(self.project, "last_us_ref", self.__class__) - - super(UserStory, self).save(*args, **kwargs) - class Change(models.Model): change_type = models.IntegerField(choices=TASK_CHANGE_CHOICES) @@ -403,8 +397,14 @@ class Issue(models.Model): # Model related signals handlers + @receiver(models.signals.post_save, sender=Project, dispatch_uid="project_post_save") def project_post_save(sender, instance, created, **kwargs): + """ + Create all project model depences on project is + created. + """ + from greenmine.base.services import RoleGroupsService if not created: @@ -439,6 +439,17 @@ def project_post_save(sender, instance, created, **kwargs): IssueType.objects.create(project=instance, name=name, order=order) +@receiver(models.signals.pre_save, sender=UserStory, dispatch_uid="user_story_ref_handler") +def user_story_ref_handler(sender, instance, **kwargs): + """ + Automatically assignes a seguent reference code to a + user story if that is not created. + """ + + if not instance.id and instance.project: + instance.ref = ref_uniquely(instance.project, "last_us_ref", instance.__class__) + + # Email alerts signals handlers # TODO: temporary commented (Pending refactor) # from . import sigdispatch diff --git a/greenmine/scrum/serializers.py b/greenmine/scrum/serializers.py index 691de5ab..eef2b442 100644 --- a/greenmine/scrum/serializers.py +++ b/greenmine/scrum/serializers.py @@ -26,7 +26,7 @@ class ProjectSerializer(serializers.ModelSerializer): class UserStorySerializer(serializers.ModelSerializer): tags = PickleField() - is_closed = serializers.BooleanField() + is_closed = serializers.Field(source='is_closed') class Meta: model = UserStory From 660932c0f5b787578859c9a298cf6bb11ac7f18c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 30 Mar 2013 23:14:50 +0100 Subject: [PATCH 2/9] Minor tuning on scrum admin part. --- greenmine/scrum/admin.py | 9 +++++++-- greenmine/scrum/serializers.py | 10 ++++++---- greenmine/settings/__init__.py | 18 ++++++------------ greenmine/urls.py | 3 +++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/greenmine/scrum/admin.py b/greenmine/scrum/admin.py index 60f760b0..442ac762 100644 --- a/greenmine/scrum/admin.py +++ b/greenmine/scrum/admin.py @@ -39,7 +39,8 @@ admin.site.register(models.Milestone, MilestoneAdmin) class UserStoryAdmin(reversion.VersionAdmin): - list_display = ["ref", "milestone", "project", "owner", 'status', 'is_closed'] + list_display = ["id", "ref", "milestone", "project", "owner", 'status', 'is_closed'] + list_filter = ["milestone", "project"] admin.site.register(models.UserStory, UserStoryAdmin) @@ -57,7 +58,11 @@ admin.site.register(models.ChangeAttachment, ChangeAttachmentAdmin) class TaskAdmin(reversion.VersionAdmin): - list_display = ["subject", "user_story"] + list_display = ["subject", "user_story", "milestone", "project", "user_story_id"] + list_filter = ["user_story", "milestone", "project"] + + def user_story_id(self, instance): + return instance.user_story.id class IssueAdmin(reversion.VersionAdmin): diff --git a/greenmine/scrum/serializers.py b/greenmine/scrum/serializers.py index eef2b442..d3246f0d 100644 --- a/greenmine/scrum/serializers.py +++ b/greenmine/scrum/serializers.py @@ -16,6 +16,12 @@ class PickleField(serializers.WritableField): return data +class PointsSerializer(serializers.ModelSerializer): + class Meta: + model = Points + fields = () + + class ProjectSerializer(serializers.ModelSerializer): tags = PickleField() @@ -111,7 +117,3 @@ class IssueTypeSerializer(serializers.ModelSerializer): fields = () -class PointsSerializer(serializers.ModelSerializer): - class Meta: - model = Points - fields = () diff --git a/greenmine/settings/__init__.py b/greenmine/settings/__init__.py index 2201e479..da0350a5 100644 --- a/greenmine/settings/__init__.py +++ b/greenmine/settings/__init__.py @@ -3,15 +3,9 @@ from __future__ import absolute_import import os -if "GREENMINE_ENVIRON" in os.environ: - if os.environ["GREENMINE_ENVIRON"] in ('production', 'development', 'local'): - print "importing %s" % os.environ["GREENMINE_ENVIRON"] - eval("from .%s import *" % (os.environ["GREENMINE_ENVIRON"])) - -else: - try: - print "Trying import local.py settings..." - from .local import * - except ImportError: - print "Trying import development.py settings..." - from .development import * +try: + print "Trying import local.py settings..." + from .local import * +except ImportError: + print "Trying import development.py settings..." + from .development import * diff --git a/greenmine/urls.py b/greenmine/urls.py index 4136a7e7..cdf74ee0 100644 --- a/greenmine/urls.py +++ b/greenmine/urls.py @@ -1,4 +1,6 @@ +# -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url +from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib import admin admin.autodiscover() @@ -13,3 +15,4 @@ urlpatterns = patterns('', url(r'^grappelli/', include('grappelli.urls')), ) +urlpatterns += staticfiles_urlpatterns() From 90b5888fbcde0c28b9f74915716cc8212125ca22 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 04:06:09 +0200 Subject: [PATCH 3/9] Create new auth backend for rest_framework based on session, but without csrf. --- greenmine/base/auth.py | 24 ++++++++++++++++++++++++ greenmine/settings/common.py | 10 +++++----- 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 greenmine/base/auth.py diff --git a/greenmine/base/auth.py b/greenmine/base/auth.py new file mode 100644 index 00000000..6ca71405 --- /dev/null +++ b/greenmine/base/auth.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from rest_framework.authentication import BaseAuthentication + + +class SessionAuthentication(BaseAuthentication): + """ + Use Django's session framework for authentication without csrf. + """ + + def authenticate(self, request): + """ + Returns a `User` if the request session currently has a logged in user. + Otherwise returns `None`. + """ + + http_request = request._request + user = getattr(http_request, 'user', None) + + if not user or not user.is_active: + return None + + return (user, None) + diff --git a/greenmine/settings/common.py b/greenmine/settings/common.py index 5ee18652..32755125 100644 --- a/greenmine/settings/common.py +++ b/greenmine/settings/common.py @@ -163,13 +163,13 @@ TEMPLATE_LOADERS = [ MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'greenmine.base.middleware.GreenmineSessionMiddleware', 'greenmine.base.middleware.CoorsMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', + #'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + #'django.contrib.messages.middleware.MessageMiddleware', + #'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.transaction.TransactionMiddleware', 'reversion.middleware.RevisionMiddleware', ] @@ -320,7 +320,7 @@ HAYSTACK_DEFAULT_OPERATOR = 'AND' REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.SessionAuthentication', + 'greenmine.base.auth.SessionAuthentication', ), 'FILTER_BACKEND': 'rest_framework.filters.DjangoFilterBackend', } From 571d59e6a81febfc8816c8f30ab596fca0a51b6b Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 04:07:00 +0200 Subject: [PATCH 4/9] Put owner automaticaly on user story is created. --- greenmine/scrum/api.py | 3 +++ greenmine/scrum/models.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/greenmine/scrum/api.py b/greenmine/scrum/api.py index 12f01414..5bddcf1c 100644 --- a/greenmine/scrum/api.py +++ b/greenmine/scrum/api.py @@ -58,6 +58,9 @@ class UserStoryList(generics.ListCreateAPIView): serializer_class = UserStorySerializer filter_fields = ('project', 'milestone') + def pre_save(self, obj): + obj.owner = self.request.user + class UserStoryDetail(generics.RetrieveUpdateDestroyAPIView): model = UserStory diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index 93094906..408f3e6c 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -114,7 +114,7 @@ class Project(models.Model): created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True, auto_now=True) - owner = models.ForeignKey("base.User", related_name="projects") + owner = models.ForeignKey("base.User", related_name="projects", blank=True) public = models.BooleanField(default=True) last_us_ref = models.BigIntegerField(null=True, default=1) @@ -191,7 +191,7 @@ class Milestone(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) name = models.CharField(max_length=200, db_index=True) slug = models.SlugField(max_length=250, unique=True, blank=True) - owner = models.ForeignKey('base.User', related_name="milestones") + owner = models.ForeignKey('base.User', related_name="milestones", blank=True) project = models.ForeignKey('Project', related_name="milestones") estimated_start = models.DateField(null=True, default=None) @@ -234,12 +234,12 @@ class Milestone(models.Model): class UserStory(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) - ref = models.BigIntegerField(db_index=True, null=True, default=None) + ref = models.BigIntegerField(db_index=True, null=True, default=None, blank=True) milestone = models.ForeignKey("Milestone", blank=True, related_name="user_stories", null=True, default=None) project = models.ForeignKey("Project", related_name="user_stories") - owner = models.ForeignKey("base.User", null=True, default=None, + owner = models.ForeignKey("base.User", blank=True, null=True, related_name="user_stories") status = models.ForeignKey("UserStoryStatus", related_name="userstories") From 95174c4d9cf358823369764c7141d431ac99b963 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 18:23:48 +0200 Subject: [PATCH 5/9] Add .ctags file. --- .ctags | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .ctags diff --git a/.ctags b/.ctags new file mode 100644 index 00000000..a7e85040 --- /dev/null +++ b/.ctags @@ -0,0 +1,5 @@ +--language-force=python +--exclude=".git" +--exclude="tags" +--exclude="*.pyc" +-R From 43f2b87d0d2ef0c7632e2c68cbb038e1730566bb Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 18:34:52 +0200 Subject: [PATCH 6/9] Add issues to api. --- greenmine/base/api.py | 1 + greenmine/scrum/api.py | 15 +++++++++++++++ greenmine/scrum/serializers.py | 8 ++++++++ greenmine/scrum/urls.py | 2 ++ 4 files changed, 26 insertions(+) diff --git a/greenmine/base/api.py b/greenmine/base/api.py index cb0050a9..1dfaf6b8 100644 --- a/greenmine/base/api.py +++ b/greenmine/base/api.py @@ -26,6 +26,7 @@ class ApiRoot(APIView): 'changes': reverse('change-list', request=request, format=format), 'change-attachments': reverse('change-attachment-list', request=request, format=format), 'tasks': reverse('task-list', request=request, format=format), + 'issues': reverse('issue-list', request=request, format=format), 'severities': reverse('severity-list', request=request, format=format), 'issue-status': reverse('issue-status-list', request=request, format=format), 'task-status': reverse('task-status-list', request=request, format=format), diff --git a/greenmine/scrum/api.py b/greenmine/scrum/api.py index 5bddcf1c..4593f33f 100644 --- a/greenmine/scrum/api.py +++ b/greenmine/scrum/api.py @@ -98,6 +98,21 @@ class TaskDetail(generics.RetrieveUpdateDestroyAPIView): serializer_class = TaskSerializer +class IssueList(generics.ListCreateAPIView): + model = Issue + serializer_class = IssueSerializer + #filter_fields = ('project') + + def pre_save(self, obj): + obj.owner = self.request.user + + +class IssueDetail(generics.RetrieveUpdateDestroyAPIView): + model = Issue + serializer_class = IssueSerializer + filter_fields = ('project',) + + class SeverityList(generics.ListCreateAPIView): model = Severity serializer_class = SeveritySerializer diff --git a/greenmine/scrum/serializers.py b/greenmine/scrum/serializers.py index d3246f0d..4b76e4ab 100644 --- a/greenmine/scrum/serializers.py +++ b/greenmine/scrum/serializers.py @@ -81,6 +81,14 @@ class IssueSerializer(serializers.ModelSerializer): fields = () +class IssueSerializer(serializers.ModelSerializer): + tags = PickleField() + + class Meta: + model = Issue + fields = () + + class SeveritySerializer(serializers.ModelSerializer): class Meta: model = Severity diff --git a/greenmine/scrum/urls.py b/greenmine/scrum/urls.py index e01bece8..b0eb1d24 100644 --- a/greenmine/scrum/urls.py +++ b/greenmine/scrum/urls.py @@ -16,6 +16,8 @@ urlpatterns = format_suffix_patterns(patterns('', url(r'^change_attachments/(?P[0-9]+)/$', api.ChangeAttachmentDetail.as_view(), name='change-attachment-detail'), url(r'^tasks/$', api.TaskList.as_view(), name='task-list'), url(r'^tasks/(?P[0-9]+)/$', api.TaskDetail.as_view(), name='task-detail'), + url(r'^issues/$', api.IssueList.as_view(), name='issue-list'), + url(r'^issues/(?P[0-9]+)/$', api.IssueDetail.as_view(), name='issue-detail'), url(r'^severities/$', api.SeverityList.as_view(), name='severity-list'), url(r'^severities/(?P[0-9]+)/$', api.SeverityDetail.as_view(), name='severity-detail'), url(r'^issue_status/$', api.IssueStatusList.as_view(), name='issue-status-list'), From 9b39a0db28082f4d1f250db5608afca18817f0a6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 18:35:08 +0200 Subject: [PATCH 7/9] Minor model and api fixes. --- greenmine/scrum/api.py | 14 ++++++++++++++ greenmine/scrum/models.py | 14 ++------------ greenmine/scrum/serializers.py | 3 +-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/greenmine/scrum/api.py b/greenmine/scrum/api.py index 4593f33f..a1fc8f5d 100644 --- a/greenmine/scrum/api.py +++ b/greenmine/scrum/api.py @@ -36,6 +36,8 @@ class ProjectList(generics.ListCreateAPIView): model = Project serializer_class = ProjectSerializer + def pre_save(self, obj): + obj.owner = self.request.user class ProjectDetail(generics.RetrieveUpdateDestroyAPIView): model = Project @@ -47,6 +49,9 @@ class MilestoneList(generics.ListCreateAPIView): serializer_class = MilestoneSerializer filter_fields = ('project',) + def pre_save(self, obj): + obj.owner = self.request.user + class MilestoneDetail(generics.RetrieveUpdateDestroyAPIView): model = Milestone @@ -71,6 +76,9 @@ class ChangeList(generics.ListCreateAPIView): model = Change serializer_class = ChangeSerializer + def pre_save(self, obj): + obj.owner = self.request.user + class ChangeDetail(generics.RetrieveUpdateDestroyAPIView): model = Change @@ -81,6 +89,9 @@ class ChangeAttachmentList(generics.ListCreateAPIView): model = ChangeAttachment serializer_class = ChangeAttachmentSerializer + def pre_save(self, obj): + obj.owner = self.request.user + class ChangeAttachmentDetail(generics.RetrieveUpdateDestroyAPIView): model = ChangeAttachment @@ -92,6 +103,9 @@ class TaskList(generics.ListCreateAPIView): serializer_class = TaskSerializer filter_fields = ('user_story', 'milestone', 'project') + def pre_save(self, obj): + obj.owner = self.request.user + class TaskDetail(generics.RetrieveUpdateDestroyAPIView): model = Task diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index 408f3e6c..2f5046b5 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -191,7 +191,8 @@ class Milestone(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) name = models.CharField(max_length=200, db_index=True) slug = models.SlugField(max_length=250, unique=True, blank=True) - owner = models.ForeignKey('base.User', related_name="milestones", blank=True) + owner = models.ForeignKey('base.User', related_name="milestones", + null=True, blank=True) project = models.ForeignKey('Project', related_name="milestones") estimated_start = models.DateField(null=True, default=None) @@ -204,8 +205,6 @@ class Milestone(models.Model): disponibility = models.FloatField(null=True, default=0.0) order = models.PositiveSmallIntegerField("Order", default=1) - tags = PickledObjectField() - def save(self, *args, **kwargs): if not self.slug: self.slug = slugify_uniquely(self.name, self.__class__) @@ -216,15 +215,6 @@ class Milestone(models.Model): ordering = ['-created_date'] unique_together = ('name', 'project') - @property - def total_points(self): - """ - Get total story points for this milestone. - """ - - total = sum(iter_points(self.user_stories.all())) - return "{0:.1f}".format(total) - def __unicode__(self): return self.name diff --git a/greenmine/scrum/serializers.py b/greenmine/scrum/serializers.py index 4b76e4ab..95008644 100644 --- a/greenmine/scrum/serializers.py +++ b/greenmine/scrum/serializers.py @@ -41,8 +41,7 @@ class UserStorySerializer(serializers.ModelSerializer): class MilestoneSerializer(serializers.ModelSerializer): - tags = PickleField() - user_stories = UserStorySerializer(many=True) + user_stories = UserStorySerializer(many=True, required=False) class Meta: model = Milestone From 7e9d1a03804145a1c0dbe32960110ea7536f982d Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 31 Mar 2013 22:59:32 +0200 Subject: [PATCH 8/9] Typo's fixes. --- greenmine/scrum/api.py | 1 - greenmine/scrum/management/commands/sample_data.py | 2 +- greenmine/scrum/models.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/greenmine/scrum/api.py b/greenmine/scrum/api.py index a1fc8f5d..2a05628e 100644 --- a/greenmine/scrum/api.py +++ b/greenmine/scrum/api.py @@ -124,7 +124,6 @@ class IssueList(generics.ListCreateAPIView): class IssueDetail(generics.RetrieveUpdateDestroyAPIView): model = Issue serializer_class = IssueSerializer - filter_fields = ('project',) class SeverityList(generics.ListCreateAPIView): diff --git a/greenmine/scrum/management/commands/sample_data.py b/greenmine/scrum/management/commands/sample_data.py index 650b94ad..bae8a828 100644 --- a/greenmine/scrum/management/commands/sample_data.py +++ b/greenmine/scrum/management/commands/sample_data.py @@ -133,7 +133,7 @@ class Command(BaseCommand): owner=project.owner, severity=Severity.objects.get(project=project, order=2), status=IssueStatus.objects.get(project=project, order=4), - priority=Priority.objects.get(project=project, order=2), + priority=Priority.objects.get(project=project, order=3), type=IssueType.objects.get(project=project, order=1), tags=[], ) diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index 2f5046b5..14c25a4e 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -416,7 +416,7 @@ def project_post_save(sender, instance, created, **kwargs): UserStoryStatus.objects.create(name=name, order=order, is_closed=is_closed, project=instance) - for order, name in POINTS_CHOICES: + for order, name in PRIORITY_CHOICES: Priority.objects.create(project=instance, name=name, order=order) for order, name in SEVERITY_CHOICES: From deee53f78f4d19d712aa3457379e254eb2726f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 1 Apr 2013 09:49:02 +0200 Subject: [PATCH 9/9] changing language-force paramter on ctags with languages parameter --- .ctags | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ctags b/.ctags index a7e85040..ab23a05e 100644 --- a/.ctags +++ b/.ctags @@ -1,5 +1,5 @@ ---language-force=python ---exclude=".git" +--languages=python +--exclude=".git" --exclude="tags" --exclude="*.pyc" -R