From bb595b8fddf8092c6e53698a9a54f5b6d2a126e3 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 19 May 2014 10:14:57 +0200 Subject: [PATCH] Reference refactor. (Merged from stable/reference-refactor) --- settings/common.py | 1 + taiga/base/resolver/api.py | 80 ----- ...0003_auto__del_unique_issue_ref_project.py | 237 ++++++++++++++ taiga/projects/issues/models.py | 8 +- ..._ref__del_field_project_last_issue_ref_.py | 235 ++++++++++++++ taiga/projects/models.py | 6 - .../references}/__init__.py | 0 taiga/projects/references/api.py | 40 +++ .../references/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/migrate_references.py | 171 +++++++++++ .../references/migrations/0001_initial.py | 45 +++ .../references/migrations/__init__.py | 0 taiga/projects/references/models.py | 72 +++++ taiga/projects/references/sequences.py | 44 +++ taiga/projects/references/serializers.py | 9 + .../0003_auto__del_unique_task_ref_project.py | 290 ++++++++++++++++++ taiga/projects/tasks/models.py | 11 +- ..._auto__del_unique_userstory_ref_project.py | 269 ++++++++++++++++ taiga/projects/userstories/models.py | 8 +- taiga/routers.py | 2 +- 21 files changed, 1417 insertions(+), 111 deletions(-) delete mode 100644 taiga/base/resolver/api.py create mode 100644 taiga/projects/issues/migrations/0003_auto__del_unique_issue_ref_project.py create mode 100644 taiga/projects/migrations/0021_auto__del_field_project_last_us_ref__del_field_project_last_issue_ref_.py rename taiga/{base/resolver => projects/references}/__init__.py (100%) create mode 100644 taiga/projects/references/api.py create mode 100644 taiga/projects/references/management/__init__.py create mode 100644 taiga/projects/references/management/commands/__init__.py create mode 100644 taiga/projects/references/management/commands/migrate_references.py create mode 100644 taiga/projects/references/migrations/0001_initial.py create mode 100644 taiga/projects/references/migrations/__init__.py create mode 100644 taiga/projects/references/models.py create mode 100644 taiga/projects/references/sequences.py create mode 100644 taiga/projects/references/serializers.py create mode 100644 taiga/projects/tasks/migrations/0003_auto__del_unique_task_ref_project.py create mode 100644 taiga/projects/userstories/migrations/0008_auto__del_unique_userstory_ref_project.py diff --git a/settings/common.py b/settings/common.py index cdbe0834..54254bcb 100644 --- a/settings/common.py +++ b/settings/common.py @@ -184,6 +184,7 @@ INSTALLED_APPS = [ "taiga.projects.userstories", "taiga.projects.tasks", "taiga.projects.issues", + "taiga.projects.references", "taiga.projects.wiki", "taiga.projects.history", "taiga.projects.notifications", diff --git a/taiga/base/resolver/api.py b/taiga/base/resolver/api.py deleted file mode 100644 index 6e15db24..00000000 --- a/taiga/base/resolver/api.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from django.db.models.loading import get_model - -from rest_framework.response import Response -from rest_framework import viewsets -from rest_framework.permissions import IsAuthenticated - -from taiga.base import exceptions as excp - - -class ResolverViewSet(viewsets.ViewSet): - permission_classes = (IsAuthenticated,) - - def list(self, request, **kwargs): - project_model = get_model("projects", "Project") - userstory_model = get_model("userstories", "UserStory") - task_model = get_model("tasks", "Task") - issue_model = get_model("issues", "Issue") - milestone_model = get_model("milestones", "Milestone") - - project_slug = request.QUERY_PARAMS.get('project', None) - us_ref = request.QUERY_PARAMS.get('us', None) - task_ref = request.QUERY_PARAMS.get('task', None) - issue_ref = request.QUERY_PARAMS.get('issue', None) - milestone_slug = request.QUERY_PARAMS.get('milestone', None) - - if project_slug is None: - return Response({}) - - try: - project = project_model.objects.get(slug=project_slug) - except project_model.DoesNotExist: - return Response({}) - - result = {"project": project.id} - - if us_ref is not None: - try: - us = project.user_stories.get(ref=us_ref) - result["us"] = us.id - except userstory_model.DoesNotExist: - pass - - if task_ref is not None: - try: - task = project.tasks.get(ref=task_ref) - result["task"] = task.id - except task_model.DoesNotExist: - pass - - if issue_ref is not None: - try: - issue = project.issues.get(ref=issue_ref) - result["issue"] = issue.id - except issue_model.DoesNotExist: - pass - - if milestone_slug is not None: - try: - milestone = project.milestones.get(slug=milestone_slug) - result["milestone"] = milestone.id - except milestone_model.DoesNotExist: - pass - - return Response(result) diff --git a/taiga/projects/issues/migrations/0003_auto__del_unique_issue_ref_project.py b/taiga/projects/issues/migrations/0003_auto__del_unique_issue_ref_project.py new file mode 100644 index 00000000..4398cf32 --- /dev/null +++ b/taiga/projects/issues/migrations/0003_auto__del_unique_issue_ref_project.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Removing unique constraint on 'Issue', fields ['ref', 'project'] + db.delete_unique('issues_issue', ['ref', 'project_id']) + + + def backwards(self, orm): + # Adding unique constraint on 'Issue', fields ['ref', 'project'] + db.create_unique('issues_issue', ['ref', 'project_id']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['auth.Permission']", 'symmetrical': 'False'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'domains.domain': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Domain'}, + 'alias_of': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['domains.Domain']", 'null': 'True', 'default': 'None', 'related_name': "'+'"}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'public_register': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'scheme': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '60'}) + }, + 'issues.issue': { + 'Meta': {'ordering': "['project', 'created_date']", 'object_name': 'Issue'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None', 'related_name': "'issues_assigned_to_me'"}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['milestones.Milestone']", 'null': 'True', 'default': 'None', 'related_name': "'issues'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None', 'related_name': "'owned_issues'"}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Priority']", 'related_name': "'issues'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issues'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'blank': 'True', 'db_index': 'True', 'null': 'True', 'default': 'None'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Severity']", 'related_name': "'issues'"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueStatus']", 'related_name': "'issues'"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueType']", 'related_name': "'issues'"}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['users.User']", 'symmetrical': 'False', 'null': 'True', 'related_name': "'watched_issues'"}) + }, + 'milestones.milestone': { + 'Meta': {'unique_together': "(('name', 'project'),)", 'ordering': "['project', 'created_date']", 'object_name': 'Milestone'}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': '0.0'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}), + 'estimated_start': ('django.db.models.fields.DateField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'related_name': "'owned_milestones'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'milestones'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}) + }, + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'ordering': "['project', 'role']", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True', 'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None', 'related_name': "'memberships'"}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.IssueStatus']", 'unique': 'True', 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.IssueType']", 'unique': 'True', 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.Points']", 'unique': 'True', 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.Priority']", 'unique': 'True', 'related_name': "'+'"}), + 'default_question_status': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.QuestionStatus']", 'unique': 'True', 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.Severity']", 'unique': 'True', 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.TaskStatus']", 'unique': 'True', 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'to': "orm['projects.UserStoryStatus']", 'unique': 'True', 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['domains.Domain']", 'null': 'True', 'default': 'None', 'related_name': "'projects'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_issue_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'last_task_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'last_us_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['users.User']", 'symmetrical': 'False', 'through': "orm['projects.Membership']", 'related_name': "'projects'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'unique': 'True', 'max_length': '250'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': '0'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'null': 'True', 'default': 'None'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.questionstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'QuestionStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'question_status'"}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'related_name': "'roles'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "'#19ba05'", 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'default': "''", 'max_length': '20'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['auth.Group']", 'symmetrical': 'False', 'related_name': "'user_set'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'notify_level': ('django.db.models.fields.CharField', [], {'default': "'all_owned_projects'", 'max_length': '32'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '200'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'to': "orm['auth.Permission']", 'symmetrical': 'False', 'related_name': "'user_set'"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + } + } + + complete_apps = ['issues'] \ No newline at end of file diff --git a/taiga/projects/issues/models.py b/taiga/projects/issues/models.py index ba92ae7f..feea578e 100644 --- a/taiga/projects/issues/models.py +++ b/taiga/projects/issues/models.py @@ -69,7 +69,7 @@ class Issue(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model): verbose_name = "issue" verbose_name_plural = "issues" ordering = ["project", "created_date"] - unique_together = ("ref", "project") + #unique_together = ("ref", "project") permissions = ( ("view_issue", "Can view issue"), ) @@ -267,12 +267,6 @@ def issue_finished_date_handler(sender, instance, **kwargs): instance.finished_date = None -@receiver(models.signals.pre_save, sender=Issue, dispatch_uid="issue_ref_handler") -def issue_ref_handler(sender, instance, **kwargs): - if not instance.id and instance.project: - instance.ref = ref_uniquely(instance.project, "last_issue_ref", instance.__class__) - - @receiver(models.signals.pre_save, sender=Issue, dispatch_uid="issue-tags-normalization") def issue_tags_normalization(sender, instance, **kwargs): if isinstance(instance.tags, (list, tuple)): diff --git a/taiga/projects/migrations/0021_auto__del_field_project_last_us_ref__del_field_project_last_issue_ref_.py b/taiga/projects/migrations/0021_auto__del_field_project_last_us_ref__del_field_project_last_issue_ref_.py new file mode 100644 index 00000000..489d3952 --- /dev/null +++ b/taiga/projects/migrations/0021_auto__del_field_project_last_us_ref__del_field_project_last_issue_ref_.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + depends_on = ( + ('references', '0001_initial'), + ) + + def forwards(self, orm): + # Deleting field 'Project.last_us_ref' + db.delete_column('projects_project', 'last_us_ref') + + # Deleting field 'Project.last_issue_ref' + db.delete_column('projects_project', 'last_issue_ref') + + # Deleting field 'Project.last_task_ref' + db.delete_column('projects_project', 'last_task_ref') + + + def backwards(self, orm): + # Adding field 'Project.last_us_ref' + db.add_column('projects_project', 'last_us_ref', + self.gf('django.db.models.fields.BigIntegerField')(default=0, null=True), + keep_default=False) + + # Adding field 'Project.last_issue_ref' + db.add_column('projects_project', 'last_issue_ref', + self.gf('django.db.models.fields.BigIntegerField')(default=0, null=True), + keep_default=False) + + # Adding field 'Project.last_task_ref' + db.add_column('projects_project', 'last_task_ref', + self.gf('django.db.models.fields.BigIntegerField')(default=0, null=True), + keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'domains.domain': { + 'Meta': {'object_name': 'Domain', 'ordering': "('domain',)"}, + 'alias_of': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['domains.Domain']"}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'public_register': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'scheme': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '60', 'null': 'True'}) + }, + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'IssueType', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'unique_together': "(('user', 'project', 'email'),)", 'object_name': 'Membership', 'ordering': "['project', 'role']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '60', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'memberships'", 'null': 'True', 'blank': 'True', 'to': "orm['users.User']"}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Points', 'ordering': "['project', 'order', 'name']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Priority', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creation_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'projects'", 'null': 'True', 'blank': 'True', 'to': "orm['projects.ProjectTemplate']"}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueStatus']", 'unique': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.IssueType']", 'unique': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Points']", 'unique': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Priority']", 'unique': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.Severity']", 'unique': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.TaskStatus']", 'unique': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'null': 'True', 'blank': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['projects.UserStoryStatus']", 'unique': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'projects'", 'null': 'True', 'blank': 'True', 'to': "orm['domains.Domain']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'projects'", 'to': "orm['users.User']", 'through': "orm['projects.Membership']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250', 'unique': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True', 'unique': 'True'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}) + }, + 'projects.projecttemplate': { + 'Meta': {'unique_together': "(['slug', 'domain'],)", 'object_name': 'ProjectTemplate', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'default_options': ('django_pgjson.fields.JsonField', [], {}), + 'default_owner_role': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'templates'", 'null': 'True', 'blank': 'True', 'to': "orm['domains.Domain']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'issue_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'issue_types': ('django_pgjson.fields.JsonField', [], {}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), + 'points': ('django_pgjson.fields.JsonField', [], {}), + 'priorities': ('django_pgjson.fields.JsonField', [], {}), + 'roles': ('django_pgjson.fields.JsonField', [], {}), + 'severities': ('django_pgjson.fields.JsonField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}), + 'task_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'us_statuses': ('django_pgjson.fields.JsonField', [], {}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Severity', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'TaskStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'UserStoryStatus', 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'object_name': 'Role', 'ordering': "['order', 'slug']"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'roles'", 'to': "orm['auth.Permission']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '250', 'blank': 'True'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#a1402e'", 'max_length': '9', 'blank': 'True'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'notify_level': ('django.db.models.fields.CharField', [], {'default': "'all_owned_projects'", 'max_length': '32'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + } + } + + complete_apps = ['projects'] diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 88d1ce0e..622f443f 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -128,12 +128,6 @@ class Project(ProjectDefaults, models.Model): through="Membership", verbose_name=_("members")) public = models.BooleanField(default=True, null=False, blank=True, verbose_name=_("public")) - last_us_ref = models.BigIntegerField(null=True, blank=False, default=0, - verbose_name=_("last us ref")) - last_task_ref = models.BigIntegerField(null=True, blank=False, default=0, - verbose_name=_("last task ref")) - last_issue_ref = models.BigIntegerField(null=True, blank=False, default=0, - verbose_name=_("last issue ref")) total_milestones = models.IntegerField(default=0, null=True, blank=True, verbose_name=_("total of milestones")) total_story_points = models.FloatField(default=None, null=True, blank=False, diff --git a/taiga/base/resolver/__init__.py b/taiga/projects/references/__init__.py similarity index 100% rename from taiga/base/resolver/__init__.py rename to taiga/projects/references/__init__.py diff --git a/taiga/projects/references/api.py b/taiga/projects/references/api.py new file mode 100644 index 00000000..7cd3c3ad --- /dev/null +++ b/taiga/projects/references/api.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +from django.db.models.loading import get_model +from django.shortcuts import get_object_or_404 + +from rest_framework.response import Response +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated + +from taiga.base import exceptions as exc +from .serializers import ResolverSerializer + + +class ResolverViewSet(viewsets.ViewSet): + permission_classes = (IsAuthenticated,) + + def list(self, request, **kwargs): + serializer = ResolverSerializer(data=request.QUERY_PARAMS) + if not serializer.is_valid(): + raise exc.BadRequest(serializer.errors) + + data = serializer.data + + project_model = get_model("projects", "Project") + project = get_object_or_404(project_model, slug=data["project"]) + + result = { + "project": project.pk + } + + if data["us"]: + result["us"] = get_object_or_404(project.user_stories.all(), ref=data["us"]).pk + if data["task"]: + result["us"] = get_object_or_404(project.tasks.all(), ref=data["task"]).pk + if data["issue"]: + result["issue"] = get_object_or_404(project.issues.all(), ref=data["issue"]).pk + if data["milestone"]: + result["milestone"] = get_object_or_404(project.milestones.all(), slug=data["milestones"]).pk + + return Response(result) diff --git a/taiga/projects/references/management/__init__.py b/taiga/projects/references/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/references/management/commands/__init__.py b/taiga/projects/references/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/references/management/commands/migrate_references.py b/taiga/projects/references/management/commands/migrate_references.py new file mode 100644 index 00000000..2bfc9c2b --- /dev/null +++ b/taiga/projects/references/management/commands/migrate_references.py @@ -0,0 +1,171 @@ +import re +from collections import defaultdict + +from django.core.management.base import BaseCommand, CommandError +from django.core.paginator import Paginator +from django.db.models.loading import get_model +from django.db.models import signals +from django.db import models +from django.db import transaction as tx + +from reversion import get_unique_for_object + +from taiga.projects.references.models import make_reference + + +class Command(BaseCommand): + help = "Migrate old references to new references system." + + def iter_queryset(self, queryset): + paginator = Paginator(queryset, 20) + for page_num in paginator.page_range: + page = paginator.page(page_num) + for element in page.object_list: + yield element + + def iter_object_versions(self, instance): + revs = get_unique_for_object(instance) + for rev in revs: + yield rev + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.refmap = defaultdict(lambda: {"us": {}, "task": {}, "issue": {},}) + + @tx.atomic + def handle(self, *args, **options): + self.disable_signals() + self.process_userstories_refs() + self.process_issues_refs() + self.process_tasks_refs() + + self.process_userstories_comments() + self.process_issues_comments() + self.process_tasks_comments() + self.process_wikipages() + + def replace_references_on_text(self, ppk, text): + matches = re.findall(r"(\:((?:us|issue|task))\:(\d+))", text) + for val, t, ref in matches: + ref = int(ref) + if ref not in self.refmap[ppk][t]: + continue + + newref = self.refmap[ppk][t][ref] + text = text.replace(val, ":{}:{}".format(t, newref)) + + matches = re.findall(r"(((?:US|Issue|Task)) \#(\d+))", text) + for val, t, ref in matches: + ref = int(ref) + if ref not in self.refmap[ppk][t.lower()]: + continue + + newref = self.refmap[ppk][t.lower()][ref] + text = text.replace(val, "{} #{}".format(t, newref)) + + return text + + def disable_signals(self): + print(".. Disabling signals.") + issue_model = get_model("issues", "Issue") + us_model = get_model("userstories", "UserStory") + task_model = get_model("tasks", "Task") + project_model = get_model("projects", "Project") + + models.signals.post_save.disconnect(dispatch_uid="refus", sender=us_model) + models.signals.post_save.disconnect(dispatch_uid="refissue", sender=issue_model) + models.signals.post_save.disconnect(dispatch_uid="reftask", sender=task_model) + models.signals.post_save.disconnect(dispatch_uid="refproj", sender=project_model) + + def process_userstories_comments(self): + print(".. Processing userstory comments.") + model_cls = get_model("userstories", "UserStory") + + for item in self.iter_queryset(model_cls.objects.all()): + item.description = self.replace_references_on_text(item.project_id, item.description) + item.blocked_note = self.replace_references_on_text(item.project_id, item.blocked_note) + item.save() + + for rev in self.iter_object_versions(item): + rev.revision.comment = self.replace_references_on_text(item.project_id, rev.revision.comment) + rev.revision.save() + rev.save() + + def process_issues_comments(self): + print(".. Processing issues comments.") + model_cls = get_model("issues", "Issue") + + for item in self.iter_queryset(model_cls.objects.all()): + item.description = self.replace_references_on_text(item.project_id, item.description) + item.blocked_note = self.replace_references_on_text(item.project_id, item.blocked_note) + item.save() + + for rev in self.iter_object_versions(item): + rev.revision.comment = self.replace_references_on_text(item.project_id, rev.revision.comment) + rev.revision.save() + rev.save() + + def process_tasks_comments(self): + print(".. Processing task comments.") + model_cls = get_model("tasks", "Task") + + for item in self.iter_queryset(model_cls.objects.all()): + item.description = self.replace_references_on_text(item.project_id, item.description) + item.blocked_note = self.replace_references_on_text(item.project_id, item.blocked_note) + item.save() + + for rev in self.iter_object_versions(item): + rev.revision.comment = self.replace_references_on_text(item.project_id, rev.revision.comment) + rev.revision.save() + rev.save() + + def process_wikipages(self): + print(".. Processing wikipages.") + model_cls = get_model("wiki", "WikiPage") + + for item in self.iter_queryset(model_cls.objects.all()): + item.content = self.replace_references_on_text(item.project_id, item.content) + item.save() + + for rev in self.iter_object_versions(item): + rev.revision.comment = self.replace_references_on_text(item.project_id, rev.revision.comment) + rev.revision.save() + rev.save() + + def process_userstories_refs(self): + print(".. Processing userstories.") + model_cls = get_model("userstories", "UserStory") + queryset = model_cls.objects.only("ref") + + for item in self.iter_queryset(queryset): + refval, _ = make_reference(item, item.project, create=True) + print("process us {0}: {1} -> {2}".format(item.pk, item.ref, refval)) + self.refmap[item.project_id]["us"][item.ref] = refval + item.ref = refval + item.save(update_fields=["ref"]) + + def process_tasks_refs(self): + print(".. Processing tasks.") + model_cls = get_model("tasks", "Task") + queryset = model_cls.objects.only("ref") + + for item in self.iter_queryset(queryset): + refval, _ = make_reference(item, item.project, create=True) + print("process task {0}: {1} -> {2}".format(item.pk, item.ref, refval)) + self.refmap[item.project_id]["task"][item.ref] = refval + item.ref = refval + item.save(update_fields=["ref"]) + + def process_issues_refs(self): + print(".. Processing issues.") + model_cls = get_model("issues", "Issue") + queryset = model_cls.objects.all() + + for item in self.iter_queryset(queryset): + refval, _ = make_reference(item, item.project, create=True) + print("process issue {0}: {1} -> {2}".format(item.pk, item.ref, refval)) + self.refmap[item.project_id]["issue"][item.ref] = refval + item.ref = refval + item.save() + + diff --git a/taiga/projects/references/migrations/0001_initial.py b/taiga/projects/references/migrations/0001_initial.py new file mode 100644 index 00000000..b05fccad --- /dev/null +++ b/taiga/projects/references/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Reference' + db.create_table('references_reference', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['contenttypes.ContentType'])), + ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('ref', self.gf('django.db.models.fields.BigIntegerField')()), + ('created_at', self.gf('django.db.models.fields.DateTimeField')(blank=True, auto_now_add=True)), + )) + db.send_create_signal('references', ['Reference']) + + + def backwards(self, orm): + # Deleting model 'Reference' + db.delete_table('references_reference') + + + models = { + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'references.reference': { + 'Meta': {'ordering': "['created_at']", 'object_name': 'Reference'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['contenttypes.ContentType']"}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {}) + } + } + + complete_apps = ['references'] diff --git a/taiga/projects/references/migrations/__init__.py b/taiga/projects/references/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/references/models.py b/taiga/projects/references/models.py new file mode 100644 index 00000000..b33b2390 --- /dev/null +++ b/taiga/projects/references/models.py @@ -0,0 +1,72 @@ +from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.generic import GenericForeignKey + +from taiga.projects.userstories.models import UserStory +from taiga.projects.tasks.models import Task +from taiga.projects.issues.models import Issue +from taiga.projects.models import Project + +from . import sequences as seq + + +class Reference(models.Model): + content_type = models.ForeignKey(ContentType, related_name="+") + object_id = models.PositiveIntegerField() + ref = models.BigIntegerField() + content_object = GenericForeignKey("content_type", "object_id") + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ["created_at"] + + def __str__(self): + return "Reference {}".format(self.object_id) + + +def make_sequence_name(project) -> str: + return "references_project{0}".format(project.pk) + + +def make_unique_reference_id(project, *, create=False): + seqname = make_sequence_name(project) + if create and not seq.exists(seqname): + seq.create(seqname) + return seq.next_value(seqname) + + +def make_reference(instance, project, create=False): + refval = make_unique_reference_id(project, create=create) + ct = ContentType.objects.get_for_model(instance.__class__) + refinstance = Reference.objects.create(content_type=ct, + object_id=instance.pk, + ref=refval) + return refval, refinstance + + +def create_sequence(sender, instance, created, **kwargs): + if not created: + return + + seqname = make_sequence_name(instance) + if not seq.exists(seqname): + seq.create(seqname) + +def attach_sequence(sender, instance, **kwargs): + # Create a reference object. This operation should be + # used in transaction context, otherwise it can + # create a lot of phantom reference objects. + refval, _ = make_reference(instance, instance.project) + + # Additionally, attach sequence number to instance as ref + sender.objects.filter(pk=instance.pk).update(ref=refval) + + +models.signals.post_save.connect(create_sequence, sender=Project, dispatch_uid="refproj") +models.signals.post_save.connect(attach_sequence, sender=UserStory, dispatch_uid="refus") +models.signals.post_save.connect(attach_sequence, sender=Issue, dispatch_uid="refissue") +models.signals.post_save.connect(attach_sequence, sender=Task, dispatch_uid="reftask") + + + + diff --git a/taiga/projects/references/sequences.py b/taiga/projects/references/sequences.py new file mode 100644 index 00000000..5d6eca8c --- /dev/null +++ b/taiga/projects/references/sequences.py @@ -0,0 +1,44 @@ +from contextlib import closing +from django.db import connection +from django.db import ProgrammingError + + +def create(seqname:str, start=1) -> None: + sql = "CREATE SEQUENCE {0} START %s".format(seqname) + + with closing(connection.cursor()) as cursor: + cursor.execute(sql, [start]) + + +def exists(seqname:str) -> bool: + sql = """ + SELECT EXISTS( + SELECT relname FROM pg_class + WHERE relkind = 'S' AND relname = %s); + """ + + with closing(connection.cursor()) as cursor: + cursor.execute(sql, [seqname]) + result = cursor.fetchone() + return result[0] + + +def alter(seqname:str, value:int) -> None: + sql = "SELECT setval(%s, %s);" + with closing(connection.cursor()) as cursor: + cursor.execute(sql, [seqname, value]) + + +def delete(seqname:str) -> None: + sql = "DROP SEQUENCE {0};".format(seqname) + with closing(connection.cursor()) as cursor: + cursor.execute(sql) + +def next_value(seqname): + sql = "SELECT nextval(%s);" + with closing(connection.cursor()) as cursor: + cursor.execute(sql, [seqname]) + result = cursor.fetchone() + return result[0] + + diff --git a/taiga/projects/references/serializers.py b/taiga/projects/references/serializers.py new file mode 100644 index 00000000..9c8ea035 --- /dev/null +++ b/taiga/projects/references/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + + +class ResolverSerializer(serializers.Serializer): + project = serializers.CharField(max_length=512, required=True) + milestone = serializers.CharField(max_length=512, required=False) + us = serializers.IntegerField(required=False) + task = serializers.IntegerField(required=False) + issue = serializers.IntegerField(required=False) diff --git a/taiga/projects/tasks/migrations/0003_auto__del_unique_task_ref_project.py b/taiga/projects/tasks/migrations/0003_auto__del_unique_task_ref_project.py new file mode 100644 index 00000000..7f56ca30 --- /dev/null +++ b/taiga/projects/tasks/migrations/0003_auto__del_unique_task_ref_project.py @@ -0,0 +1,290 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Removing unique constraint on 'Task', fields ['ref', 'project'] + db.delete_unique('tasks_task', ['ref', 'project_id']) + + + def backwards(self, orm): + # Adding unique constraint on 'Task', fields ['ref', 'project'] + db.create_unique('tasks_task', ['ref', 'project_id']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'db_table': "'django_content_type'", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'ordering': "('name',)"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'domains.domain': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Domain'}, + 'alias_of': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'blank': 'True', 'to': "orm['domains.Domain']", 'null': 'True', 'default': 'None'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'public_register': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'scheme': ('django.db.models.fields.CharField', [], {'null': 'True', 'default': 'None', 'max_length': '60'}) + }, + 'issues.issue': { + 'Meta': {'ordering': "['project', 'created_date']", 'object_name': 'Issue'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues_assigned_to_me'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'blank': 'True', 'to': "orm['milestones.Milestone']", 'null': 'True', 'default': 'None'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_issues'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Priority']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'blank': 'True', 'null': 'True', 'default': 'None'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.Severity']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueStatus']"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['projects.IssueType']"}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'watched_issues'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True'}) + }, + 'milestones.milestone': { + 'Meta': {'unique_together': "(('name', 'project'),)", 'ordering': "['project', 'created_date']", 'object_name': 'Milestone'}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': '0.0'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}), + 'estimated_start': ('django.db.models.fields.DateField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_milestones'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'milestones'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'blank': 'True', 'max_length': '250'}) + }, + 'projects.issuestatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.issuetype': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'IssueType'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issue_types'", 'to': "orm['projects.Project']"}) + }, + 'projects.membership': { + 'Meta': {'ordering': "['project', 'role']", 'object_name': 'Membership'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True', 'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['projects.Project']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'to': "orm['users.Role']"}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'memberships'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}) + }, + 'projects.points': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Points'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'points'", 'to': "orm['projects.Project']"}), + 'value': ('django.db.models.fields.FloatField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'projects.priority': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Priority'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'priorities'", 'to': "orm['projects.Project']"}) + }, + 'projects.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.IssueStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.IssueType']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.Points']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.Priority']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_question_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.QuestionStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.Severity']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.TaskStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'to': "orm['projects.UserStoryStatus']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'unique': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'blank': 'True', 'to': "orm['domains.Domain']", 'null': 'True', 'default': 'None'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_issue_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'last_task_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'last_us_ref': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'default': '0'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'projects'", 'to': "orm['users.User']", 'through': "orm['projects.Membership']"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_projects'", 'to': "orm['users.User']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'blank': 'True', 'max_length': '250'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': '0'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'null': 'True', 'default': 'None'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '250'}) + }, + 'projects.questionstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'QuestionStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_status'", 'to': "orm['projects.Project']"}) + }, + 'projects.severity': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'Severity'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'severities'", 'to': "orm['projects.Project']"}) + }, + 'projects.taskstatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'TaskStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_statuses'", 'to': "orm['projects.Project']"}) + }, + 'projects.userstorystatus': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']", 'object_name': 'UserStoryStatus'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '20', 'default': "'#999999'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'us_statuses'", 'to': "orm['projects.Project']"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True', 'default': 'None'}) + }, + 'tasks.task': { + 'Meta': {'ordering': "['project', 'created_date']", 'object_name': 'Task'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks_assigned_to_me'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_iocaine': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'blank': 'True', 'to': "orm['milestones.Milestone']", 'null': 'True', 'default': 'None'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'owned_tasks'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'blank': 'True', 'null': 'True', 'default': 'None'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': "orm['projects.TaskStatus']"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'blank': 'True', 'to': "orm['userstories.UserStory']", 'null': 'True'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'watched_tasks'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True'}) + }, + 'users.role': { + 'Meta': {'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']", 'object_name': 'Role'}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'roles'", 'to': "orm['auth.Permission']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'roles'", 'to': "orm['projects.Project']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'color': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '9', 'default': "'#888337'"}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '20', 'default': "''"}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'notify_level': ('django.db.models.fields.CharField', [], {'max_length': '32', 'default': "'all_owned_projects'"}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'default': 'None', 'max_length': '200'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'userstories.rolepoints': { + 'Meta': {'unique_together': "(('user_story', 'role'),)", 'ordering': "['user_story', 'role']", 'object_name': 'RolePoints'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'points': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['projects.Points']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['users.Role']"}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_points'", 'to': "orm['userstories.UserStory']"}) + }, + 'userstories.userstory': { + 'Meta': {'ordering': "['project', 'order', 'ref']", 'object_name': 'UserStory'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userstories_assigned_to_me'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True', 'default': 'None'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'client_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finish_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'generated_from_issue': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'generated_user_stories'", 'blank': 'True', 'to': "orm['issues.Issue']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_stories'", 'on_delete': 'models.SET_NULL', 'null': 'True', 'default': 'None', 'to': "orm['milestones.Milestone']", 'blank': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_user_stories'", 'blank': 'True', 'on_delete': 'models.SET_NULL', 'null': 'True'}), + 'points': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'userstories'", 'to': "orm['projects.Points']", 'through': "orm['userstories.RolePoints']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_stories'", 'to': "orm['projects.Project']"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'blank': 'True', 'null': 'True', 'default': 'None'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.UserStoryStatus']", 'related_name': "'user_stories'", 'blank': 'True', 'on_delete': 'models.SET_NULL', 'null': 'True'}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'team_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'watched_user_stories'", 'blank': 'True', 'to': "orm['users.User']", 'null': 'True'}) + } + } + + complete_apps = ['tasks'] \ No newline at end of file diff --git a/taiga/projects/tasks/models.py b/taiga/projects/tasks/models.py index e90129e1..efa6f9b1 100644 --- a/taiga/projects/tasks/models.py +++ b/taiga/projects/tasks/models.py @@ -67,7 +67,7 @@ class Task(WatchedMixin, BlockedMixin): verbose_name = "task" verbose_name_plural = "tasks" ordering = ["project", "created_date"] - unique_together = ("ref", "project") + # unique_together = ("ref", "project") permissions = ( ("view_task", "Can view task"), ) @@ -94,15 +94,6 @@ class Task(WatchedMixin, BlockedMixin): } -# Model related signals handlers -@receiver(models.signals.pre_save, sender=Task, dispatch_uid="task_ref_handler") -def task_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_task_ref", instance.__class__) def us_has_open_tasks(us, exclude_task): qs = us.tasks.all() diff --git a/taiga/projects/userstories/migrations/0008_auto__del_unique_userstory_ref_project.py b/taiga/projects/userstories/migrations/0008_auto__del_unique_userstory_ref_project.py new file mode 100644 index 00000000..26d5f039 --- /dev/null +++ b/taiga/projects/userstories/migrations/0008_auto__del_unique_userstory_ref_project.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Removing unique constraint on 'UserStory', fields ['ref', 'project'] + db.delete_unique('userstories_userstory', ['ref', 'project_id']) + + + def backwards(self, orm): + # Adding unique constraint on 'UserStory', fields ['ref', 'project'] + db.create_unique('userstories_userstory', ['ref', 'project_id']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'symmetrical': 'False', 'to': "orm['auth.Permission']"}) + }, + 'auth.permission': { + 'Meta': {'object_name': 'Permission', 'unique_together': "(('content_type', 'codename'),)", 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'object_name': 'ContentType', 'db_table': "'django_content_type'", 'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'domains.domain': { + 'Meta': {'object_name': 'Domain', 'ordering': "('domain',)"}, + 'alias_of': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['domains.Domain']", 'related_name': "'+'", 'null': 'True'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'public_register': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'scheme': ('django.db.models.fields.CharField', [], {'default': 'None', 'null': 'True', 'max_length': '60'}) + }, + 'issues.issue': { + 'Meta': {'object_name': 'Issue', 'ordering': "['project', 'created_date']"}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'issues_assigned_to_me'", 'null': 'True'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finished_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['milestones.Milestone']", 'related_name': "'issues'", 'null': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'owned_issues'", 'null': 'True'}), + 'priority': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Priority']", 'related_name': "'issues'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issues'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'severity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Severity']", 'related_name': "'issues'"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueStatus']", 'related_name': "'issues'"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.IssueType']", 'related_name': "'issues'"}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'blank': 'True', 'symmetrical': 'False', 'to': "orm['users.User']", 'related_name': "'watched_issues'"}) + }, + 'milestones.milestone': { + 'Meta': {'object_name': 'Milestone', 'unique_together': "(('name', 'project'),)", 'ordering': "['project', 'created_date']"}, + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'disponibility': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'blank': 'True', 'null': 'True'}), + 'estimated_finish': ('django.db.models.fields.DateField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}), + 'estimated_start': ('django.db.models.fields.DateField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'owned_milestones'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'milestones'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '250', 'blank': 'True'}) + }, + 'projects.issuestatus': { + 'Meta': {'object_name': 'IssueStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_statuses'"}) + }, + 'projects.issuetype': { + 'Meta': {'object_name': 'IssueType', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'issue_types'"}) + }, + 'projects.membership': { + 'Meta': {'object_name': 'Membership', 'ordering': "['project', 'role']"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True', 'auto_now_add': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'memberships'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'memberships'"}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '60'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'memberships'", 'null': 'True'}) + }, + 'projects.points': { + 'Meta': {'object_name': 'Points', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'points'"}), + 'value': ('django.db.models.fields.FloatField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}) + }, + 'projects.priority': { + 'Meta': {'object_name': 'Priority', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'priorities'"}) + }, + 'projects.project': { + 'Meta': {'object_name': 'Project', 'ordering': "['name']"}, + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'default_issue_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.IssueStatus']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_issue_type': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.IssueType']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_points': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.Points']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_priority': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.Priority']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_question_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.QuestionStatus']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_severity': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.Severity']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_task_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.TaskStatus']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'default_us_status': ('django.db.models.fields.related.OneToOneField', [], {'unique': 'True', 'to': "orm['projects.UserStoryStatus']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'+'"}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['domains.Domain']", 'related_name': "'projects'", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_backlog_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_issues_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_kanban_activated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_wiki_activated': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_issue_ref': ('django.db.models.fields.BigIntegerField', [], {'default': '0', 'null': 'True'}), + 'last_task_ref': ('django.db.models.fields.BigIntegerField', [], {'default': '0', 'null': 'True'}), + 'last_us_ref': ('django.db.models.fields.BigIntegerField', [], {'default': '0', 'null': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['projects.Membership']", 'symmetrical': 'False', 'to': "orm['users.User']", 'related_name': "'projects'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.User']", 'related_name': "'owned_projects'"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '250', 'blank': 'True'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'total_milestones': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True', 'null': 'True'}), + 'total_story_points': ('django.db.models.fields.FloatField', [], {'default': 'None', 'null': 'True'}), + 'videoconferences': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}), + 'videoconferences_salt': ('django.db.models.fields.CharField', [], {'null': 'True', 'blank': 'True', 'max_length': '250'}) + }, + 'projects.questionstatus': { + 'Meta': {'object_name': 'QuestionStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'question_status'"}) + }, + 'projects.severity': { + 'Meta': {'object_name': 'Severity', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'severities'"}) + }, + 'projects.taskstatus': { + 'Meta': {'object_name': 'TaskStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'task_statuses'"}) + }, + 'projects.userstorystatus': { + 'Meta': {'object_name': 'UserStoryStatus', 'unique_together': "(('project', 'name'),)", 'ordering': "['project', 'order', 'name']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#999999'", 'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'us_statuses'"}), + 'wip_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'blank': 'True', 'null': 'True'}) + }, + 'users.role': { + 'Meta': {'object_name': 'Role', 'unique_together': "(('slug', 'project'),)", 'ordering': "['order', 'slug']"}, + 'computable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.Permission']", 'related_name': "'roles'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'roles'"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '250'}) + }, + 'users.user': { + 'Meta': {'object_name': 'User', 'ordering': "['username']"}, + 'color': ('django.db.models.fields.CharField', [], {'default': "'#1ecac2'", 'blank': 'True', 'max_length': '9'}), + 'colorize_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_language': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'default_timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'blank': 'True', 'max_length': '20'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'symmetrical': 'False', 'to': "orm['auth.Group']", 'related_name': "'user_set'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'notify_changes_by_me': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'notify_level': ('django.db.models.fields.CharField', [], {'default': "'all_owned_projects'", 'max_length': '32'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'photo': ('django.db.models.fields.files.FileField', [], {'null': 'True', 'blank': 'True', 'max_length': '500'}), + 'token': ('django.db.models.fields.CharField', [], {'default': 'None', 'blank': 'True', 'null': 'True', 'max_length': '200'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'symmetrical': 'False', 'to': "orm['auth.Permission']", 'related_name': "'user_set'"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'userstories.rolepoints': { + 'Meta': {'object_name': 'RolePoints', 'unique_together': "(('user_story', 'role'),)", 'ordering': "['user_story', 'role']"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'points': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Points']", 'related_name': "'role_points'"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.Role']", 'related_name': "'role_points'"}), + 'user_story': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['userstories.UserStory']", 'related_name': "'role_points'"}) + }, + 'userstories.userstory': { + 'Meta': {'object_name': 'UserStory', 'ordering': "['project', 'order', 'ref']"}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'userstories_assigned_to_me'", 'null': 'True'}), + 'blocked_note': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'client_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'finish_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'generated_from_issue': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['issues.Issue']", 'related_name': "'generated_user_stories'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['milestones.Milestone']", 'on_delete': 'models.SET_NULL', 'null': 'True', 'blank': 'True', 'related_name': "'user_stories'"}), + 'modified_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['users.User']", 'related_name': "'owned_user_stories'", 'on_delete': 'models.SET_NULL'}), + 'points': ('django.db.models.fields.related.ManyToManyField', [], {'through': "orm['userstories.RolePoints']", 'symmetrical': 'False', 'to': "orm['projects.Points']", 'related_name': "'userstories'"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Project']", 'related_name': "'user_stories'"}), + 'ref': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'blank': 'True', 'to': "orm['projects.UserStoryStatus']", 'related_name': "'user_stories'", 'on_delete': 'models.SET_NULL'}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'tags': ('picklefield.fields.PickledObjectField', [], {'blank': 'True'}), + 'team_requirement': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'watchers': ('django.db.models.fields.related.ManyToManyField', [], {'null': 'True', 'blank': 'True', 'symmetrical': 'False', 'to': "orm['users.User']", 'related_name': "'watched_user_stories'"}) + } + } + + complete_apps = ['userstories'] \ No newline at end of file diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index 88f36774..f404ddba 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -101,7 +101,7 @@ class UserStory(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model): verbose_name = "user story" verbose_name_plural = "user stories" ordering = ["project", "order", "ref"] - unique_together = ("ref", "project") + #unique_together = ("ref", "project") permissions = ( ("view_userstory", "Can view user story"), ) @@ -159,12 +159,6 @@ class UserStory(NeighborsMixin, WatchedMixin, BlockedMixin, models.Model): } -# Model related signals handlers -@receiver(models.signals.pre_save, sender=UserStory, dispatch_uid="user_story_ref_handler") -def us_ref_handler(sender, instance, **kwargs): - if not instance.id and instance.project: - instance.ref = ref_uniquely(instance.project, "last_us_ref", instance.__class__) - @receiver(models.signals.post_save, sender=UserStory, dispatch_uid="user_story_create_role_points_handler") diff --git a/taiga/routers.py b/taiga/routers.py index 4057565e..dda1af13 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -36,7 +36,7 @@ router.register(r"user-storage", StorageEntriesViewSet, base_name="user-storage" # Resolver & Search from taiga.base.searches.api import SearchViewSet -from taiga.base.resolver.api import ResolverViewSet +from taiga.projects.references.api import ResolverViewSet router.register(r"resolver", ResolverViewSet, base_name="resolver") router.register(r"search", SearchViewSet, base_name="search")