Adding votes and watched projects to sample_data
parent
f3641f5cfb
commit
bccdc2fae1
|
@ -31,7 +31,7 @@ from taiga.base.api.utils import get_object_or_404
|
||||||
from taiga.base.utils.slug import slugify_uniquely
|
from taiga.base.utils.slug import slugify_uniquely
|
||||||
|
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
|
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
|
||||||
from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin
|
from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin
|
||||||
|
|
||||||
|
@ -270,6 +270,10 @@ class ProjectFansViewSet(VotersViewSetMixin, ModelListViewSet):
|
||||||
resource_model = models.Project
|
resource_model = models.Project
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.ProjectWatchersPermission,)
|
||||||
|
resource_model = models.Project
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
## Custom values for selectors
|
## Custom values for selectors
|
||||||
######################################################
|
######################################################
|
||||||
|
|
|
@ -27,7 +27,7 @@ from taiga.base.api.utils import get_object_or_404
|
||||||
|
|
||||||
from taiga.users.models import User
|
from taiga.users.models import User
|
||||||
|
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
|
|
||||||
|
@ -243,3 +243,8 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W
|
||||||
class IssueVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
class IssueVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
||||||
permission_classes = (permissions.IssueVotersPermission,)
|
permission_classes = (permissions.IssueVotersPermission,)
|
||||||
resource_model = models.Issue
|
resource_model = models.Issue
|
||||||
|
|
||||||
|
|
||||||
|
class IssueWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.IssueWatchersPermission,)
|
||||||
|
resource_model = models.Issue
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
from django.contrib.contenttypes.management import update_all_contenttypes
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
def create_notifications(apps, schema_editor):
|
||||||
update_all_contenttypes()
|
update_all_contenttypes()
|
||||||
migrations.RunSQL(sql="""
|
sql="""
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id)
|
||||||
SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id
|
||||||
FROM issues_issue_watchers""".format(content_type_id=ContentType.objects.get(model='issue').id))
|
FROM issues_issue_watchers INNER JOIN issues_issue ON issues_issue_watchers.issue_id = issues_issue.id""".format(content_type_id=ContentType.objects.get(model='issue').id)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -52,3 +52,10 @@ class IssueVotersPermission(TaigaResourcePermission):
|
||||||
global_perms = None
|
global_perms = None
|
||||||
retrieve_perms = HasProjectPerm('view_issues')
|
retrieve_perms = HasProjectPerm('view_issues')
|
||||||
list_perms = HasProjectPerm('view_issues')
|
list_perms = HasProjectPerm('view_issues')
|
||||||
|
|
||||||
|
|
||||||
|
class IssueWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_issues')
|
||||||
|
list_perms = HasProjectPerm('view_issues')
|
||||||
|
|
|
@ -27,7 +27,7 @@ from taiga.base.utils import db, text
|
||||||
from taiga.projects.issues.apps import (
|
from taiga.projects.issues.apps import (
|
||||||
connect_issues_signals,
|
connect_issues_signals,
|
||||||
disconnect_issues_signals)
|
disconnect_issues_signals)
|
||||||
|
from taiga.projects.votes import services as votes_services
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ def issues_to_csv(project, queryset):
|
||||||
fieldnames = ["ref", "subject", "description", "milestone", "owner",
|
fieldnames = ["ref", "subject", "description", "milestone", "owner",
|
||||||
"owner_full_name", "assigned_to", "assigned_to_full_name",
|
"owner_full_name", "assigned_to", "assigned_to_full_name",
|
||||||
"status", "severity", "priority", "type", "is_closed",
|
"status", "severity", "priority", "type", "is_closed",
|
||||||
"attachments", "external_reference", "tags"]
|
"attachments", "external_reference", "tags",
|
||||||
|
"watchers", "voters"]
|
||||||
for custom_attr in project.issuecustomattributes.all():
|
for custom_attr in project.issuecustomattributes.all():
|
||||||
fieldnames.append(custom_attr.name)
|
fieldnames.append(custom_attr.name)
|
||||||
|
|
||||||
|
@ -108,6 +109,8 @@ def issues_to_csv(project, queryset):
|
||||||
"attachments": issue.attachments.count(),
|
"attachments": issue.attachments.count(),
|
||||||
"external_reference": issue.external_reference,
|
"external_reference": issue.external_reference,
|
||||||
"tags": ",".join(issue.tags or []),
|
"tags": ",".join(issue.tags or []),
|
||||||
|
"watchers": [u.id for u in issue.get_watchers()],
|
||||||
|
"voters": votes_services.get_voters(issue).count(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for custom_attr in project.issuecustomattributes.all():
|
for custom_attr in project.issuecustomattributes.all():
|
||||||
|
|
|
@ -37,6 +37,7 @@ from taiga.projects.wiki.models import *
|
||||||
from taiga.projects.attachments.models import *
|
from taiga.projects.attachments.models import *
|
||||||
from taiga.projects.custom_attributes.models import *
|
from taiga.projects.custom_attributes.models import *
|
||||||
from taiga.projects.history.services import take_snapshot
|
from taiga.projects.history.services import take_snapshot
|
||||||
|
from taiga.projects.votes.services import add_vote
|
||||||
from taiga.events.apps import disconnect_events_signals
|
from taiga.events.apps import disconnect_events_signals
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +98,8 @@ NUM_TASKS = getattr(settings, "SAMPLE_DATA_NUM_TASKS", (0, 4))
|
||||||
NUM_USS_BACK = getattr(settings, "SAMPLE_DATA_NUM_USS_BACK", (8, 20))
|
NUM_USS_BACK = getattr(settings, "SAMPLE_DATA_NUM_USS_BACK", (8, 20))
|
||||||
NUM_ISSUES = getattr(settings, "SAMPLE_DATA_NUM_ISSUES", (12, 25))
|
NUM_ISSUES = getattr(settings, "SAMPLE_DATA_NUM_ISSUES", (12, 25))
|
||||||
NUM_ATTACHMENTS = getattr(settings, "SAMPLE_DATA_NUM_ATTACHMENTS", (0, 4))
|
NUM_ATTACHMENTS = getattr(settings, "SAMPLE_DATA_NUM_ATTACHMENTS", (0, 4))
|
||||||
|
NUM_VOTES = getattr(settings, "SAMPLE_DATA_NUM_VOTES", (0, 3))
|
||||||
|
NUM_PROJECT_WATCHERS = getattr(settings, "SAMPLE_DATA_NUM_PROJECT_WATCHERS", (0, 3))
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
sd = SampleDataHelper(seed=12345678901)
|
sd = SampleDataHelper(seed=12345678901)
|
||||||
|
@ -215,6 +217,7 @@ class Command(BaseCommand):
|
||||||
project.total_story_points = int(defined_points * self.sd.int(5,12) / 10)
|
project.total_story_points = int(defined_points * self.sd.int(5,12) / 10)
|
||||||
project.save()
|
project.save()
|
||||||
|
|
||||||
|
self.create_votes(project, project)
|
||||||
|
|
||||||
def create_attachment(self, obj, order):
|
def create_attachment(self, obj, order):
|
||||||
attached_file = self.sd.file_from_directory(*ATTACHMENT_SAMPLE_DATA)
|
attached_file = self.sd.file_from_directory(*ATTACHMENT_SAMPLE_DATA)
|
||||||
|
@ -287,7 +290,7 @@ class Command(BaseCommand):
|
||||||
bug.save()
|
bug.save()
|
||||||
|
|
||||||
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
||||||
bug.watchers.add(watching_user)
|
bug.add_watcher(watching_user)
|
||||||
|
|
||||||
take_snapshot(bug,
|
take_snapshot(bug,
|
||||||
comment=self.sd.paragraph(),
|
comment=self.sd.paragraph(),
|
||||||
|
@ -300,6 +303,7 @@ class Command(BaseCommand):
|
||||||
comment=self.sd.paragraph(),
|
comment=self.sd.paragraph(),
|
||||||
user=bug.owner)
|
user=bug.owner)
|
||||||
|
|
||||||
|
self.create_votes(bug, project)
|
||||||
return bug
|
return bug
|
||||||
|
|
||||||
def create_task(self, project, milestone, us, min_date, max_date, closed=False):
|
def create_task(self, project, milestone, us, min_date, max_date, closed=False):
|
||||||
|
@ -338,7 +342,7 @@ class Command(BaseCommand):
|
||||||
user=task.owner)
|
user=task.owner)
|
||||||
|
|
||||||
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
||||||
task.watchers.add(watching_user)
|
task.add_watcher(watching_user)
|
||||||
|
|
||||||
# Add history entry
|
# Add history entry
|
||||||
task.status=self.sd.db_object_from_queryset(project.task_statuses.all())
|
task.status=self.sd.db_object_from_queryset(project.task_statuses.all())
|
||||||
|
@ -347,6 +351,7 @@ class Command(BaseCommand):
|
||||||
comment=self.sd.paragraph(),
|
comment=self.sd.paragraph(),
|
||||||
user=task.owner)
|
user=task.owner)
|
||||||
|
|
||||||
|
self.create_votes(task, project)
|
||||||
return task
|
return task
|
||||||
|
|
||||||
def create_us(self, project, milestone=None, computable_project_roles=[]):
|
def create_us(self, project, milestone=None, computable_project_roles=[]):
|
||||||
|
@ -387,7 +392,7 @@ class Command(BaseCommand):
|
||||||
us.save()
|
us.save()
|
||||||
|
|
||||||
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user
|
||||||
us.watchers.add(watching_user)
|
us.add_watcher(watching_user)
|
||||||
|
|
||||||
take_snapshot(us,
|
take_snapshot(us,
|
||||||
comment=self.sd.paragraph(),
|
comment=self.sd.paragraph(),
|
||||||
|
@ -400,6 +405,7 @@ class Command(BaseCommand):
|
||||||
comment=self.sd.paragraph(),
|
comment=self.sd.paragraph(),
|
||||||
user=us.owner)
|
user=us.owner)
|
||||||
|
|
||||||
|
self.create_votes(us, project)
|
||||||
return us
|
return us
|
||||||
|
|
||||||
def create_milestone(self, project, start_date, end_date):
|
def create_milestone(self, project, start_date, end_date):
|
||||||
|
@ -434,6 +440,11 @@ class Command(BaseCommand):
|
||||||
project.is_kanban_activated = True
|
project.is_kanban_activated = True
|
||||||
project.save()
|
project.save()
|
||||||
take_snapshot(project, user=project.owner)
|
take_snapshot(project, user=project.owner)
|
||||||
|
|
||||||
|
for i in range(self.sd.int(*NUM_PROJECT_WATCHERS)):
|
||||||
|
watching_user = self.sd.db_object_from_queryset(User.objects.all())
|
||||||
|
project.add_watcher(watching_user)
|
||||||
|
|
||||||
return project
|
return project
|
||||||
|
|
||||||
def create_user(self, counter=None, username=None, full_name=None, email=None):
|
def create_user(self, counter=None, username=None, full_name=None, email=None):
|
||||||
|
@ -452,3 +463,8 @@ class Command(BaseCommand):
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def create_votes(self, obj, project):
|
||||||
|
for i in range(self.sd.int(*NUM_VOTES)):
|
||||||
|
voting_user=self.sd.db_object_from_queryset(project.members.all())
|
||||||
|
add_vote(obj, voting_user)
|
||||||
|
|
|
@ -14,12 +14,6 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
|
||||||
model_name='project',
|
|
||||||
name='watchers',
|
|
||||||
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, related_name='projects_project+', null=True, verbose_name='watchers'),
|
|
||||||
preserve_default=True,
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
name='public_permissions',
|
name='public_permissions',
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
|
||||||
update_all_contenttypes()
|
|
||||||
migrations.RunSQL(sql="""
|
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
|
||||||
SELECT project_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
|
||||||
FROM projects_project_watchers""".format(content_type_id=ContentType.objects.get(model='project').id))
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
('notifications', '0004_watched'),
|
|
||||||
('projects', '0024_auto_20150810_1247'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(create_notifications),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='project',
|
|
||||||
name='watchers',
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -17,10 +17,10 @@
|
||||||
from taiga.base import filters
|
from taiga.base import filters
|
||||||
from taiga.base import response
|
from taiga.base import response
|
||||||
from taiga.base.decorators import detail_route
|
from taiga.base.decorators import detail_route
|
||||||
from taiga.base.api import ModelCrudViewSet
|
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
||||||
from taiga.base.api.utils import get_object_or_404
|
from taiga.base.api.utils import get_object_or_404
|
||||||
|
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,9 +36,11 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView
|
||||||
permission_classes = (permissions.MilestonePermission,)
|
permission_classes = (permissions.MilestonePermission,)
|
||||||
filter_backends = (filters.CanViewMilestonesFilterBackend,)
|
filter_backends = (filters.CanViewMilestonesFilterBackend,)
|
||||||
filter_fields = ("project", "closed")
|
filter_fields = ("project", "closed")
|
||||||
|
queryset = models.Milestone.objects.all()
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = models.Milestone.objects.all()
|
qs = super().get_queryset()
|
||||||
|
qs = self.attach_watchers_attrs_to_queryset(qs)
|
||||||
qs = qs.prefetch_related("user_stories",
|
qs = qs.prefetch_related("user_stories",
|
||||||
"user_stories__role_points",
|
"user_stories__role_points",
|
||||||
"user_stories__role_points__points",
|
"user_stories__role_points__points",
|
||||||
|
@ -91,3 +93,8 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView
|
||||||
optimal_points -= optimal_points_per_day
|
optimal_points -= optimal_points_per_day
|
||||||
|
|
||||||
return response.Ok(milestone_stats)
|
return response.Ok(milestone_stats)
|
||||||
|
|
||||||
|
|
||||||
|
class MilestoneWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.MilestoneWatchersPermission,)
|
||||||
|
resource_model = models.Milestone
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
from django.contrib.contenttypes.management import update_all_contenttypes
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
def create_notifications(apps, schema_editor):
|
||||||
update_all_contenttypes()
|
update_all_contenttypes()
|
||||||
migrations.RunSQL(sql="""
|
sql="""
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id)
|
||||||
SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id
|
||||||
FROM milestones_milestone_watchers""".format(content_type_id=ContentType.objects.get(model='milestone').id)),
|
FROM milestones_milestone_watchers INNER JOIN milestones_milestone ON milestones_milestone_watchers.milestone_id = milestones_milestone.id""".format(content_type_id=ContentType.objects.get(model='milestone').id)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
||||||
IsProjectOwner, AllowAny,
|
IsAuthenticated, IsProjectOwner, AllowAny,
|
||||||
PermissionComponent, IsSuperUser)
|
IsSuperUser)
|
||||||
|
|
||||||
|
|
||||||
class MilestonePermission(TaigaResourcePermission):
|
class MilestonePermission(TaigaResourcePermission):
|
||||||
|
@ -29,3 +29,11 @@ class MilestonePermission(TaigaResourcePermission):
|
||||||
destroy_perms = HasProjectPerm('delete_milestone')
|
destroy_perms = HasProjectPerm('delete_milestone')
|
||||||
list_perms = AllowAny()
|
list_perms = AllowAny()
|
||||||
stats_perms = HasProjectPerm('view_milestones')
|
stats_perms = HasProjectPerm('view_milestones')
|
||||||
|
watch_perms = IsAuthenticated() & HasProjectPerm('view_milestones')
|
||||||
|
unwatch_perms = IsAuthenticated() & HasProjectPerm('view_milestones')
|
||||||
|
|
||||||
|
class MilestoneWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_milestones')
|
||||||
|
list_perms = HasProjectPerm('view_milestones')
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from taiga.base.api import serializers
|
from taiga.base.api import serializers
|
||||||
|
|
||||||
from taiga.base.utils import json
|
from taiga.base.utils import json
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceModelSerializer
|
from taiga.projects.notifications.mixins import WatchedResourceModelSerializer
|
||||||
from taiga.projects.notifications.validators import WatchersValidator
|
from taiga.projects.notifications.validators import WatchersValidator
|
||||||
|
|
|
@ -19,8 +19,9 @@ from django.db.models import Q
|
||||||
from taiga.base.api import ModelCrudViewSet
|
from taiga.base.api import ModelCrudViewSet
|
||||||
|
|
||||||
from taiga.projects.notifications.choices import NotifyLevel
|
from taiga.projects.notifications.choices import NotifyLevel
|
||||||
|
from taiga.projects.notifications.models import Watched
|
||||||
from taiga.projects.models import Project
|
from taiga.projects.models import Project
|
||||||
|
from taiga.users import services as user_services
|
||||||
from . import serializers
|
from . import serializers
|
||||||
from . import models
|
from . import models
|
||||||
from . import permissions
|
from . import permissions
|
||||||
|
@ -32,9 +33,13 @@ class NotifyPolicyViewSet(ModelCrudViewSet):
|
||||||
permission_classes = (permissions.NotifyPolicyPermission,)
|
permission_classes = (permissions.NotifyPolicyPermission,)
|
||||||
|
|
||||||
def _build_needed_notify_policies(self):
|
def _build_needed_notify_policies(self):
|
||||||
|
watched_content = user_services.get_watched_content_for_user(self.request.user)
|
||||||
|
watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct()
|
||||||
|
|
||||||
projects = Project.objects.filter(
|
projects = Project.objects.filter(
|
||||||
Q(owner=self.request.user) |
|
Q(owner=self.request.user) |
|
||||||
Q(memberships__user=self.request.user)
|
Q(memberships__user=self.request.user) |
|
||||||
|
Q(id__in=watched_content_project_ids)
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
for project in projects:
|
for project in projects:
|
||||||
|
@ -45,5 +50,14 @@ class NotifyPolicyViewSet(ModelCrudViewSet):
|
||||||
return models.NotifyPolicy.objects.none()
|
return models.NotifyPolicy.objects.none()
|
||||||
|
|
||||||
self._build_needed_notify_policies()
|
self._build_needed_notify_policies()
|
||||||
qs = models.NotifyPolicy.objects.filter(user=self.request.user)
|
|
||||||
return qs.distinct()
|
# With really want to include the policies related to any content:
|
||||||
|
# - The user is the owner of the project
|
||||||
|
# - The user is member of the project
|
||||||
|
# - The user is watching any object from the project
|
||||||
|
watched_content = user_services.get_watched_content_for_user(self.request.user)
|
||||||
|
watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct()
|
||||||
|
return models.NotifyPolicy.objects.filter(Q(project__owner=self.request.user) |
|
||||||
|
Q(project__memberships__user=self.request.user) |
|
||||||
|
Q(project__id__in=watched_content_project_ids)
|
||||||
|
).distinct()
|
||||||
|
|
|
@ -5,10 +5,6 @@ from django.db import models, migrations
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
def fill_watched_table(apps, schema_editor):
|
|
||||||
Watched = apps.get_model("notifications", "Watched")
|
|
||||||
print("test")
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
@ -21,15 +17,22 @@ class Migration(migrations.Migration):
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Watched',
|
name='Watched',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('object_id', models.PositiveIntegerField()),
|
('object_id', models.PositiveIntegerField()),
|
||||||
('created_date', models.DateTimeField(verbose_name='created date', auto_now_add=True)),
|
('created_date', models.DateTimeField(verbose_name='created date', auto_now_add=True)),
|
||||||
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
|
('content_type', models.ForeignKey(to='contenttypes.ContentType')),
|
||||||
('user', models.ForeignKey(related_name='watched', verbose_name='user', to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(related_name='watched', verbose_name='user', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('project', models.ForeignKey(to='projects.Project', verbose_name='project', related_name='watched')),
|
||||||
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
|
'verbose_name': 'Watched',
|
||||||
|
'verbose_name_plural': 'Watched',
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.RunPython(fill_watched_table),
|
migrations.AlterUniqueTogether(
|
||||||
|
name='watched',
|
||||||
|
unique_together=set([('content_type', 'object_id', 'user', 'project')]),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,17 +19,21 @@ from operator import is_not
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from taiga.base import response
|
from taiga.base import response
|
||||||
from taiga.base.decorators import detail_route
|
from taiga.base.decorators import detail_route
|
||||||
from taiga.base.api import serializers
|
from taiga.base.api import serializers
|
||||||
|
from taiga.base.api.utils import get_object_or_404
|
||||||
from taiga.base.fields import WatchersField
|
from taiga.base.fields import WatchersField
|
||||||
from taiga.projects.notifications import services
|
from taiga.projects.notifications import services
|
||||||
from taiga.projects.notifications.utils import attach_watchers_to_queryset, attach_is_watched_to_queryset
|
from taiga.projects.notifications.utils import attach_watchers_to_queryset, attach_is_watched_to_queryset
|
||||||
from taiga.users.models import User
|
from taiga.users.models import User
|
||||||
from . import models
|
from . import models
|
||||||
|
from . serializers import WatcherSerializer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WatchedResourceMixin:
|
class WatchedResourceMixin:
|
||||||
|
@ -121,7 +125,6 @@ class WatchedModelMixin(object):
|
||||||
that should works in almost all cases.
|
that should works in almost all cases.
|
||||||
"""
|
"""
|
||||||
return self.project
|
return self.project
|
||||||
t
|
|
||||||
|
|
||||||
def get_watchers(self) -> frozenset:
|
def get_watchers(self) -> frozenset:
|
||||||
"""
|
"""
|
||||||
|
@ -139,6 +142,9 @@ class WatchedModelMixin(object):
|
||||||
"""
|
"""
|
||||||
return frozenset(services.get_watchers(self))
|
return frozenset(services.get_watchers(self))
|
||||||
|
|
||||||
|
def get_watched(self, user_or_id):
|
||||||
|
return services.get_watched(user_or_id, type(self))
|
||||||
|
|
||||||
def add_watcher(self, user):
|
def add_watcher(self, user):
|
||||||
services.add_watcher(self, user)
|
services.add_watcher(self, user)
|
||||||
|
|
||||||
|
@ -209,7 +215,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer):
|
||||||
def to_native(self, obj):
|
def to_native(self, obj):
|
||||||
#watchers is wasn't attached via the get_queryset of the viewset we need to manually add it
|
#watchers is wasn't attached via the get_queryset of the viewset we need to manually add it
|
||||||
if not hasattr(obj, "watchers"):
|
if not hasattr(obj, "watchers"):
|
||||||
obj.watchers = services.get_watchers(obj)
|
obj.watchers = [user.id for user in services.get_watchers(obj)]
|
||||||
|
|
||||||
return super(WatchedResourceModelSerializer, self).to_native(obj)
|
return super(WatchedResourceModelSerializer, self).to_native(obj)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ from django.utils import timezone
|
||||||
|
|
||||||
from taiga.projects.history.choices import HISTORY_TYPE_CHOICES
|
from taiga.projects.history.choices import HISTORY_TYPE_CHOICES
|
||||||
|
|
||||||
from .choices import NOTIFY_LEVEL_CHOICES
|
from .choices import NOTIFY_LEVEL_CHOICES, NotifyLevel
|
||||||
|
|
||||||
|
|
||||||
class NotifyPolicy(models.Model):
|
class NotifyPolicy(models.Model):
|
||||||
|
@ -84,8 +84,9 @@ class Watched(models.Model):
|
||||||
related_name="watched", verbose_name=_("user"))
|
related_name="watched", verbose_name=_("user"))
|
||||||
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
||||||
verbose_name=_("created date"))
|
verbose_name=_("created date"))
|
||||||
|
project = models.ForeignKey("projects.Project", null=False, blank=False,
|
||||||
|
verbose_name=_("project"),related_name="watched")
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Watched")
|
verbose_name = _("Watched")
|
||||||
verbose_name_plural = _("Watched")
|
verbose_name_plural = _("Watched")
|
||||||
unique_together = ("content_type", "object_id", "user")
|
unique_together = ("content_type", "object_id", "user", "project")
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from taiga.base.api import serializers
|
from taiga.base.api import serializers
|
||||||
|
from taiga.users.models import User
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
from . import choices
|
||||||
|
|
||||||
|
|
||||||
class NotifyPolicySerializer(serializers.ModelSerializer):
|
class NotifyPolicySerializer(serializers.ModelSerializer):
|
||||||
|
@ -31,3 +32,11 @@ class NotifyPolicySerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def get_project_name(self, obj):
|
def get_project_name(self, obj):
|
||||||
return obj.project.name
|
return obj.project.name
|
||||||
|
|
||||||
|
|
||||||
|
class WatcherSerializer(serializers.ModelSerializer):
|
||||||
|
full_name = serializers.CharField(source='get_full_name', required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ('id', 'username', 'full_name')
|
||||||
|
|
|
@ -20,6 +20,7 @@ from django.apps import apps
|
||||||
from django.db.transaction import atomic
|
from django.db.transaction import atomic
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
@ -170,15 +171,19 @@ def get_users_to_notify(obj, *, discard_users=None) -> list:
|
||||||
candidates = set()
|
candidates = set()
|
||||||
candidates.update(filter(_can_notify_hard, project.members.all()))
|
candidates.update(filter(_can_notify_hard, project.members.all()))
|
||||||
candidates.update(filter(_can_notify_light, obj.get_watchers()))
|
candidates.update(filter(_can_notify_light, obj.get_watchers()))
|
||||||
|
candidates.update(filter(_can_notify_light, obj.project.get_watchers()))
|
||||||
candidates.update(filter(_can_notify_light, obj.get_participants()))
|
candidates.update(filter(_can_notify_light, obj.get_participants()))
|
||||||
|
|
||||||
|
#TODO: coger los watchers del proyecto que quieren ser notificados por correo
|
||||||
|
#Filtrar los watchers según su nivel de watched y su nivel en el proyecto
|
||||||
|
|
||||||
# Remove the changer from candidates
|
# Remove the changer from candidates
|
||||||
if discard_users:
|
if discard_users:
|
||||||
candidates = candidates - set(discard_users)
|
candidates = candidates - set(discard_users)
|
||||||
|
|
||||||
candidates = filter(partial(_filter_by_permissions, obj), candidates)
|
candidates = set(filter(partial(_filter_by_permissions, obj), candidates))
|
||||||
# Filter disabled and system users
|
# Filter disabled and system users
|
||||||
candidates = filter(partial(_filter_notificable), candidates)
|
candidates = set(filter(partial(_filter_notificable), candidates))
|
||||||
return frozenset(candidates)
|
return frozenset(candidates)
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,27 +290,54 @@ def process_sync_notifications():
|
||||||
|
|
||||||
|
|
||||||
def get_watchers(obj):
|
def get_watchers(obj):
|
||||||
User = apps.get_model("users", "User")
|
"""Get the watchers of an object.
|
||||||
Watched = apps.get_model("notifications", "Watched")
|
|
||||||
content_type = ContentType.objects.get_for_model(obj)
|
:param obj: Any Django model instance.
|
||||||
watching_user_ids = Watched.objects.filter(content_type=content_type, object_id=obj.id).values_list("user__id", flat=True)
|
|
||||||
return User.objects.filter(id__in=watching_user_ids)
|
:return: User queryset object representing the users that voted the object.
|
||||||
|
"""
|
||||||
|
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
||||||
|
return get_user_model().objects.filter(watched__content_type=obj_type, watched__object_id=obj.id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_watched(user_or_id, model):
|
||||||
|
"""Get the objects watched by an user.
|
||||||
|
|
||||||
|
:param user_or_id: :class:`~taiga.users.models.User` instance or id.
|
||||||
|
:param model: Show only objects of this kind. Can be any Django model class.
|
||||||
|
|
||||||
|
:return: Queryset of objects representing the votes of the user.
|
||||||
|
"""
|
||||||
|
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model)
|
||||||
|
conditions = ('notifications_watched.content_type_id = %s',
|
||||||
|
'%s.id = notifications_watched.object_id' % model._meta.db_table,
|
||||||
|
'notifications_watched.user_id = %s')
|
||||||
|
|
||||||
|
if isinstance(user_or_id, get_user_model()):
|
||||||
|
user_id = user_or_id.id
|
||||||
|
else:
|
||||||
|
user_id = user_or_id
|
||||||
|
|
||||||
|
return model.objects.extra(where=conditions, tables=('notifications_watched',),
|
||||||
|
params=(obj_type.id, user_id))
|
||||||
|
|
||||||
|
|
||||||
def add_watcher(obj, user):
|
def add_watcher(obj, user):
|
||||||
"""Add a watcher to an object.
|
"""Add a watcher to an object.
|
||||||
|
|
||||||
If the user is already watching the object nothing happends, so this function can be considered
|
If the user is already watching the object nothing happents (except if there is a level update),
|
||||||
idempotent.
|
so this function can be considered idempotent.
|
||||||
|
|
||||||
:param obj: Any Django model instance.
|
:param obj: Any Django model instance.
|
||||||
:param user: User adding the watch. :class:`~taiga.users.models.User` instance.
|
:param user: User adding the watch. :class:`~taiga.users.models.User` instance.
|
||||||
"""
|
"""
|
||||||
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
||||||
with atomic():
|
watched, created = Watched.objects.get_or_create(content_type=obj_type,
|
||||||
watched, created = Watched.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user)
|
object_id=obj.id, user=user, project=obj.project)
|
||||||
if not created:
|
|
||||||
return
|
notify_policy, _ = apps.get_model("notifications", "NotifyPolicy").objects.get_or_create(
|
||||||
|
project=obj.project, user=user, defaults={"notify_level": NotifyLevel.watch})
|
||||||
|
|
||||||
return watched
|
return watched
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,9 +351,8 @@ def remove_watcher(obj, user):
|
||||||
:param user: User removing the watch. :class:`~taiga.users.models.User` instance.
|
:param user: User removing the watch. :class:`~taiga.users.models.User` instance.
|
||||||
"""
|
"""
|
||||||
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
||||||
with atomic():
|
qs = Watched.objects.filter(content_type=obj_type, object_id=obj.id, user=user)
|
||||||
qs = Watched.objects.filter(content_type=obj_type, object_id=obj.id, user=user)
|
if not qs.exists():
|
||||||
if not qs.exists():
|
return
|
||||||
return
|
|
||||||
|
|
||||||
qs.delete()
|
qs.delete()
|
||||||
|
|
|
@ -75,6 +75,13 @@ class ProjectFansPermission(TaigaResourcePermission):
|
||||||
list_perms = HasProjectPerm('view_project')
|
list_perms = HasProjectPerm('view_project')
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_project')
|
||||||
|
list_perms = HasProjectPerm('view_project')
|
||||||
|
|
||||||
|
|
||||||
class MembershipPermission(TaigaResourcePermission):
|
class MembershipPermission(TaigaResourcePermission):
|
||||||
retrieve_perms = HasProjectPerm('view_project')
|
retrieve_perms = HasProjectPerm('view_project')
|
||||||
create_perms = IsProjectOwner()
|
create_perms = IsProjectOwner()
|
||||||
|
|
|
@ -24,7 +24,7 @@ from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
||||||
from taiga.projects.models import Project, TaskStatus
|
from taiga.projects.models import Project, TaskStatus
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
|
from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
|
||||||
|
@ -177,3 +177,8 @@ class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, Wa
|
||||||
class TaskVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
class TaskVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
||||||
permission_classes = (permissions.TaskVotersPermission,)
|
permission_classes = (permissions.TaskVotersPermission,)
|
||||||
resource_model = models.Task
|
resource_model = models.Task
|
||||||
|
|
||||||
|
|
||||||
|
class TaskWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.TaskWatchersPermission,)
|
||||||
|
resource_model = models.Task
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
from django.contrib.contenttypes.management import update_all_contenttypes
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
def create_notifications(apps, schema_editor):
|
||||||
update_all_contenttypes()
|
update_all_contenttypes()
|
||||||
migrations.RunSQL(sql="""
|
sql="""
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id)
|
||||||
SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id
|
||||||
FROM tasks_task_watchers""".format(content_type_id=ContentType.objects.get(model='task').id)),
|
FROM tasks_task_watchers INNER JOIN tasks_task ON tasks_task_watchers.task_id = tasks_task.id""".format(content_type_id=ContentType.objects.get(model='task').id)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -42,3 +42,10 @@ class TaskVotersPermission(TaigaResourcePermission):
|
||||||
global_perms = None
|
global_perms = None
|
||||||
retrieve_perms = HasProjectPerm('view_tasks')
|
retrieve_perms = HasProjectPerm('view_tasks')
|
||||||
list_perms = HasProjectPerm('view_tasks')
|
list_perms = HasProjectPerm('view_tasks')
|
||||||
|
|
||||||
|
|
||||||
|
class TaskWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_tasks')
|
||||||
|
list_perms = HasProjectPerm('view_tasks')
|
||||||
|
|
|
@ -23,6 +23,7 @@ from taiga.projects.tasks.apps import (
|
||||||
connect_tasks_signals,
|
connect_tasks_signals,
|
||||||
disconnect_tasks_signals)
|
disconnect_tasks_signals)
|
||||||
from taiga.events import events
|
from taiga.events import events
|
||||||
|
from taiga.projects.votes import services as votes_services
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
@ -95,7 +96,8 @@ def tasks_to_csv(project, queryset):
|
||||||
fieldnames = ["ref", "subject", "description", "user_story", "milestone", "owner",
|
fieldnames = ["ref", "subject", "description", "user_story", "milestone", "owner",
|
||||||
"owner_full_name", "assigned_to", "assigned_to_full_name",
|
"owner_full_name", "assigned_to", "assigned_to_full_name",
|
||||||
"status", "is_iocaine", "is_closed", "us_order",
|
"status", "is_iocaine", "is_closed", "us_order",
|
||||||
"taskboard_order", "attachments", "external_reference", "tags"]
|
"taskboard_order", "attachments", "external_reference", "tags",
|
||||||
|
"watchers", "voters"]
|
||||||
for custom_attr in project.taskcustomattributes.all():
|
for custom_attr in project.taskcustomattributes.all():
|
||||||
fieldnames.append(custom_attr.name)
|
fieldnames.append(custom_attr.name)
|
||||||
|
|
||||||
|
@ -120,6 +122,8 @@ def tasks_to_csv(project, queryset):
|
||||||
"attachments": task.attachments.count(),
|
"attachments": task.attachments.count(),
|
||||||
"external_reference": task.external_reference,
|
"external_reference": task.external_reference,
|
||||||
"tags": ",".join(task.tags or []),
|
"tags": ",".join(task.tags or []),
|
||||||
|
"watchers": [u.id for u in task.get_watchers()],
|
||||||
|
"voters": votes_services.get_voters(task).count(),
|
||||||
}
|
}
|
||||||
for custom_attr in project.taskcustomattributes.all():
|
for custom_attr in project.taskcustomattributes.all():
|
||||||
value = task.custom_attributes_values.attributes_values.get(str(custom_attr.id), None)
|
value = task.custom_attributes_values.attributes_values.get(str(custom_attr.id), None)
|
||||||
|
|
|
@ -31,7 +31,7 @@ from taiga.base.decorators import list_route
|
||||||
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
||||||
from taiga.base.api.utils import get_object_or_404
|
from taiga.base.api.utils import get_object_or_404
|
||||||
|
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
from taiga.projects.models import Project, UserStoryStatus
|
from taiga.projects.models import Project, UserStoryStatus
|
||||||
|
@ -270,3 +270,8 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi
|
||||||
class UserStoryVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
class UserStoryVotersViewSet(VotersViewSetMixin, ModelListViewSet):
|
||||||
permission_classes = (permissions.UserStoryVotersPermission,)
|
permission_classes = (permissions.UserStoryVotersPermission,)
|
||||||
resource_model = models.UserStory
|
resource_model = models.UserStory
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.UserStoryWatchersPermission,)
|
||||||
|
resource_model = models.UserStory
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
from django.contrib.contenttypes.management import update_all_contenttypes
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
def create_notifications(apps, schema_editor):
|
||||||
update_all_contenttypes()
|
update_all_contenttypes()
|
||||||
migrations.RunSQL(sql="""
|
sql="""
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id)
|
||||||
SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id
|
||||||
FROM userstories_userstory_watchers""".format(content_type_id=ContentType.objects.get(model='userstory').id)),
|
FROM userstories_userstory_watchers INNER JOIN userstories_userstory ON userstories_userstory_watchers.userstory_id = userstories_userstory.id""".format(content_type_id=ContentType.objects.get(model='userstory').id)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -35,8 +35,16 @@ class UserStoryPermission(TaigaResourcePermission):
|
||||||
watch_perms = IsAuthenticated() & HasProjectPerm('view_us')
|
watch_perms = IsAuthenticated() & HasProjectPerm('view_us')
|
||||||
unwatch_perms = IsAuthenticated() & HasProjectPerm('view_us')
|
unwatch_perms = IsAuthenticated() & HasProjectPerm('view_us')
|
||||||
|
|
||||||
|
|
||||||
class UserStoryVotersPermission(TaigaResourcePermission):
|
class UserStoryVotersPermission(TaigaResourcePermission):
|
||||||
enought_perms = IsProjectOwner() | IsSuperUser()
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
global_perms = None
|
global_perms = None
|
||||||
retrieve_perms = HasProjectPerm('view_us')
|
retrieve_perms = HasProjectPerm('view_us')
|
||||||
list_perms = HasProjectPerm('view_us')
|
list_perms = HasProjectPerm('view_us')
|
||||||
|
|
||||||
|
|
||||||
|
class UserStoryWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_us')
|
||||||
|
list_perms = HasProjectPerm('view_us')
|
||||||
|
|
|
@ -31,6 +31,7 @@ from taiga.projects.userstories.apps import (
|
||||||
disconnect_userstories_signals)
|
disconnect_userstories_signals)
|
||||||
|
|
||||||
from taiga.events import events
|
from taiga.events import events
|
||||||
|
from taiga.projects.votes import services as votes_services
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
@ -138,7 +139,8 @@ def userstories_to_csv(project,queryset):
|
||||||
"created_date", "modified_date", "finish_date",
|
"created_date", "modified_date", "finish_date",
|
||||||
"client_requirement", "team_requirement", "attachments",
|
"client_requirement", "team_requirement", "attachments",
|
||||||
"generated_from_issue", "external_reference", "tasks",
|
"generated_from_issue", "external_reference", "tasks",
|
||||||
"tags"]
|
"tags",
|
||||||
|
"watchers", "voters"]
|
||||||
|
|
||||||
for custom_attr in project.userstorycustomattributes.all():
|
for custom_attr in project.userstorycustomattributes.all():
|
||||||
fieldnames.append(custom_attr.name)
|
fieldnames.append(custom_attr.name)
|
||||||
|
@ -170,6 +172,8 @@ def userstories_to_csv(project,queryset):
|
||||||
"external_reference": us.external_reference,
|
"external_reference": us.external_reference,
|
||||||
"tasks": ",".join([str(task.ref) for task in us.tasks.all()]),
|
"tasks": ",".join([str(task.ref) for task in us.tasks.all()]),
|
||||||
"tags": ",".join(us.tags or []),
|
"tags": ",".join(us.tags or []),
|
||||||
|
"watchers": [u.id for u in us.get_watchers()],
|
||||||
|
"voters": votes_services.get_voters(us).count(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for role in us.project.roles.filter(computable=True).order_by('name'):
|
for role in us.project.roles.filter(computable=True).order_by('name'):
|
||||||
|
|
|
@ -21,13 +21,13 @@ from taiga.base.api.permissions import IsAuthenticated
|
||||||
from taiga.base import filters
|
from taiga.base import filters
|
||||||
from taiga.base import exceptions as exc
|
from taiga.base import exceptions as exc
|
||||||
from taiga.base import response
|
from taiga.base import response
|
||||||
from taiga.base.api import ModelCrudViewSet
|
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
|
||||||
from taiga.base.api.utils import get_object_or_404
|
from taiga.base.api.utils import get_object_or_404
|
||||||
from taiga.base.decorators import list_route
|
from taiga.base.decorators import list_route
|
||||||
from taiga.projects.models import Project
|
from taiga.projects.models import Project
|
||||||
from taiga.mdrender.service import render as mdrender
|
from taiga.mdrender.service import render as mdrender
|
||||||
|
|
||||||
from taiga.projects.notifications.mixins import WatchedResourceMixin
|
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
|
||||||
from taiga.projects.history.mixins import HistoryResourceMixin
|
from taiga.projects.history.mixins import HistoryResourceMixin
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
|
|
||||||
|
@ -43,6 +43,12 @@ class WikiViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
|
||||||
permission_classes = (permissions.WikiPagePermission,)
|
permission_classes = (permissions.WikiPagePermission,)
|
||||||
filter_backends = (filters.CanViewWikiPagesFilterBackend,)
|
filter_backends = (filters.CanViewWikiPagesFilterBackend,)
|
||||||
filter_fields = ("project", "slug")
|
filter_fields = ("project", "slug")
|
||||||
|
queryset = models.WikiPage.objects.all()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = super().get_queryset()
|
||||||
|
qs = self.attach_watchers_attrs_to_queryset(qs)
|
||||||
|
return qs
|
||||||
|
|
||||||
@list_route(methods=["GET"])
|
@list_route(methods=["GET"])
|
||||||
def by_slug(self, request):
|
def by_slug(self, request):
|
||||||
|
@ -77,6 +83,11 @@ class WikiViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
|
||||||
super().pre_save(obj)
|
super().pre_save(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class WikiWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
|
||||||
|
permission_classes = (permissions.WikiPageWatchersPermission,)
|
||||||
|
resource_model = models.WikiPage
|
||||||
|
|
||||||
|
|
||||||
class WikiLinkViewSet(ModelCrudViewSet):
|
class WikiLinkViewSet(ModelCrudViewSet):
|
||||||
model = models.WikiLink
|
model = models.WikiLink
|
||||||
serializer_class = serializers.WikiLinkSerializer
|
serializer_class = serializers.WikiLinkSerializer
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import apps
|
from django.db import connection
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.management import update_all_contenttypes
|
from django.contrib.contenttypes.management import update_all_contenttypes
|
||||||
|
|
||||||
def create_notifications(apps, schema_editor):
|
def create_notifications(apps, schema_editor):
|
||||||
update_all_contenttypes()
|
update_all_contenttypes()
|
||||||
migrations.RunSQL(sql="""
|
sql="""
|
||||||
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id)
|
INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id)
|
||||||
SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id
|
SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id
|
||||||
FROM wiki_wikipage_watchers""".format(content_type_id=ContentType.objects.get(model='wikipage').id)),
|
FROM wiki_wikipage_watchers INNER JOIN wiki_wikipage ON wiki_wikipage_watchers.wikipage_id = wiki_wikipage.id""".format(content_type_id=ContentType.objects.get(model='wikipage').id)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
||||||
IsProjectOwner, AllowAny, IsSuperUser)
|
IsAuthenticated, IsProjectOwner, AllowAny,
|
||||||
|
IsSuperUser)
|
||||||
|
|
||||||
|
|
||||||
class WikiPagePermission(TaigaResourcePermission):
|
class WikiPagePermission(TaigaResourcePermission):
|
||||||
|
@ -29,6 +30,16 @@ class WikiPagePermission(TaigaResourcePermission):
|
||||||
destroy_perms = HasProjectPerm('delete_wiki_page')
|
destroy_perms = HasProjectPerm('delete_wiki_page')
|
||||||
list_perms = AllowAny()
|
list_perms = AllowAny()
|
||||||
render_perms = AllowAny()
|
render_perms = AllowAny()
|
||||||
|
watch_perms = IsAuthenticated() & HasProjectPerm('view_wiki_pages')
|
||||||
|
unwatch_perms = IsAuthenticated() & HasProjectPerm('view_wiki_pages')
|
||||||
|
|
||||||
|
|
||||||
|
class WikiPageWatchersPermission(TaigaResourcePermission):
|
||||||
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
global_perms = None
|
||||||
|
retrieve_perms = HasProjectPerm('view_wiki_pages')
|
||||||
|
list_perms = HasProjectPerm('view_wiki_pages')
|
||||||
|
|
||||||
|
|
||||||
class WikiLinkPermission(TaigaResourcePermission):
|
class WikiLinkPermission(TaigaResourcePermission):
|
||||||
enought_perms = IsProjectOwner() | IsSuperUser()
|
enought_perms = IsProjectOwner() | IsSuperUser()
|
||||||
|
|
|
@ -22,10 +22,6 @@ from taiga.mdrender.service import render as mdrender
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
from taiga.projects.history import services as history_service
|
|
||||||
|
|
||||||
from taiga.mdrender.service import render as mdrender
|
|
||||||
|
|
||||||
|
|
||||||
class WikiPageSerializer(WatchersValidator, WatchedResourceModelSerializer, serializers.ModelSerializer):
|
class WikiPageSerializer(WatchersValidator, WatchedResourceModelSerializer, serializers.ModelSerializer):
|
||||||
html = serializers.SerializerMethodField("get_html")
|
html = serializers.SerializerMethodField("get_html")
|
||||||
|
|
|
@ -49,6 +49,7 @@ router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notification
|
||||||
# Projects & Selectors
|
# Projects & Selectors
|
||||||
from taiga.projects.api import ProjectViewSet
|
from taiga.projects.api import ProjectViewSet
|
||||||
from taiga.projects.api import ProjectFansViewSet
|
from taiga.projects.api import ProjectFansViewSet
|
||||||
|
from taiga.projects.api import ProjectWatchersViewSet
|
||||||
from taiga.projects.api import MembershipViewSet
|
from taiga.projects.api import MembershipViewSet
|
||||||
from taiga.projects.api import InvitationViewSet
|
from taiga.projects.api import InvitationViewSet
|
||||||
from taiga.projects.api import UserStoryStatusViewSet
|
from taiga.projects.api import UserStoryStatusViewSet
|
||||||
|
@ -62,6 +63,7 @@ from taiga.projects.api import ProjectTemplateViewSet
|
||||||
|
|
||||||
router.register(r"projects", ProjectViewSet, base_name="projects")
|
router.register(r"projects", ProjectViewSet, base_name="projects")
|
||||||
router.register(r"projects/(?P<resource_id>\d+)/fans", ProjectFansViewSet, base_name="project-fans")
|
router.register(r"projects/(?P<resource_id>\d+)/fans", ProjectFansViewSet, base_name="project-fans")
|
||||||
|
router.register(r"projects/(?P<resource_id>\d+)/watchers", ProjectWatchersViewSet, base_name="project-watchers")
|
||||||
router.register(r"project-templates", ProjectTemplateViewSet, base_name="project-templates")
|
router.register(r"project-templates", ProjectTemplateViewSet, base_name="project-templates")
|
||||||
router.register(r"memberships", MembershipViewSet, base_name="memberships")
|
router.register(r"memberships", MembershipViewSet, base_name="memberships")
|
||||||
router.register(r"invitations", InvitationViewSet, base_name="invitations")
|
router.register(r"invitations", InvitationViewSet, base_name="invitations")
|
||||||
|
@ -124,22 +126,33 @@ router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-atta
|
||||||
|
|
||||||
# Project components
|
# Project components
|
||||||
from taiga.projects.milestones.api import MilestoneViewSet
|
from taiga.projects.milestones.api import MilestoneViewSet
|
||||||
|
from taiga.projects.milestones.api import MilestoneWatchersViewSet
|
||||||
from taiga.projects.userstories.api import UserStoryViewSet
|
from taiga.projects.userstories.api import UserStoryViewSet
|
||||||
from taiga.projects.userstories.api import UserStoryVotersViewSet
|
from taiga.projects.userstories.api import UserStoryVotersViewSet
|
||||||
|
from taiga.projects.userstories.api import UserStoryWatchersViewSet
|
||||||
from taiga.projects.tasks.api import TaskViewSet
|
from taiga.projects.tasks.api import TaskViewSet
|
||||||
from taiga.projects.tasks.api import TaskVotersViewSet
|
from taiga.projects.tasks.api import TaskVotersViewSet
|
||||||
|
from taiga.projects.tasks.api import TaskWatchersViewSet
|
||||||
from taiga.projects.issues.api import IssueViewSet
|
from taiga.projects.issues.api import IssueViewSet
|
||||||
from taiga.projects.issues.api import IssueVotersViewSet
|
from taiga.projects.issues.api import IssueVotersViewSet
|
||||||
from taiga.projects.wiki.api import WikiViewSet, WikiLinkViewSet
|
from taiga.projects.issues.api import IssueWatchersViewSet
|
||||||
|
from taiga.projects.wiki.api import WikiViewSet
|
||||||
|
from taiga.projects.wiki.api import WikiLinkViewSet
|
||||||
|
from taiga.projects.wiki.api import WikiWatchersViewSet
|
||||||
|
|
||||||
router.register(r"milestones", MilestoneViewSet, base_name="milestones")
|
router.register(r"milestones", MilestoneViewSet, base_name="milestones")
|
||||||
|
router.register(r"milestones/(?P<resource_id>\d+)/watchers", MilestoneWatchersViewSet, base_name="milestone-watchers")
|
||||||
router.register(r"userstories", UserStoryViewSet, base_name="userstories")
|
router.register(r"userstories", UserStoryViewSet, base_name="userstories")
|
||||||
router.register(r"userstories/(?P<resource_id>\d+)/voters", UserStoryVotersViewSet, base_name="userstory-voters")
|
router.register(r"userstories/(?P<resource_id>\d+)/voters", UserStoryVotersViewSet, base_name="userstory-voters")
|
||||||
|
router.register(r"userstories/(?P<resource_id>\d+)/watchers", UserStoryWatchersViewSet, base_name="userstory-watchers")
|
||||||
router.register(r"tasks", TaskViewSet, base_name="tasks")
|
router.register(r"tasks", TaskViewSet, base_name="tasks")
|
||||||
router.register(r"tasks/(?P<resource_id>\d+)/voters", TaskVotersViewSet, base_name="task-voters")
|
router.register(r"tasks/(?P<resource_id>\d+)/voters", TaskVotersViewSet, base_name="task-voters")
|
||||||
|
router.register(r"tasks/(?P<resource_id>\d+)/watchers", TaskWatchersViewSet, base_name="task-watchers")
|
||||||
router.register(r"issues", IssueViewSet, base_name="issues")
|
router.register(r"issues", IssueViewSet, base_name="issues")
|
||||||
router.register(r"issues/(?P<resource_id>\d+)/voters", IssueVotersViewSet, base_name="issue-voters")
|
router.register(r"issues/(?P<resource_id>\d+)/voters", IssueVotersViewSet, base_name="issue-voters")
|
||||||
|
router.register(r"issues/(?P<resource_id>\d+)/watchers", IssueWatchersViewSet, base_name="issue-watchers")
|
||||||
router.register(r"wiki", WikiViewSet, base_name="wiki")
|
router.register(r"wiki", WikiViewSet, base_name="wiki")
|
||||||
|
router.register(r"wiki/(?P<resource_id>\d+)/watchers", WikiWatchersViewSet, base_name="wiki-watchers")
|
||||||
router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links")
|
router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,12 @@ from taiga.projects.history import services as history_services
|
||||||
from taiga.projects.models import Project
|
from taiga.projects.models import Project
|
||||||
from taiga.users.models import User
|
from taiga.users.models import User
|
||||||
from taiga.projects.history.choices import HistoryType
|
from taiga.projects.history.choices import HistoryType
|
||||||
|
from taiga.projects.notifications import services as notifications_services
|
||||||
from taiga.timeline.service import (push_to_timeline,
|
from taiga.timeline.service import (push_to_timeline,
|
||||||
build_user_namespace,
|
build_user_namespace,
|
||||||
build_project_namespace,
|
build_project_namespace,
|
||||||
extract_user_info)
|
extract_user_info)
|
||||||
|
|
||||||
# TODO: Add events to followers timeline when followers are implemented.
|
|
||||||
# TODO: Add events to project watchers timeline when project watchers are implemented.
|
|
||||||
|
|
||||||
|
|
||||||
def _push_to_timeline(*args, **kwargs):
|
def _push_to_timeline(*args, **kwargs):
|
||||||
if settings.CELERY_ENABLED:
|
if settings.CELERY_ENABLED:
|
||||||
|
@ -60,9 +58,9 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d
|
||||||
related_people |= User.objects.filter(id=obj.assigned_to_id)
|
related_people |= User.objects.filter(id=obj.assigned_to_id)
|
||||||
|
|
||||||
## - Watchers
|
## - Watchers
|
||||||
watchers = getattr(obj, "watchers", None)
|
watchers = notifications_services.get_watchers(obj)
|
||||||
if watchers:
|
if watchers:
|
||||||
related_people |= obj.get_watchers()
|
related_people |= watchers
|
||||||
|
|
||||||
## - Exclude inactive and system users and remove duplicate
|
## - Exclude inactive and system users and remove duplicate
|
||||||
related_people = related_people.exclude(is_active=False)
|
related_people = related_people.exclude(is_active=False)
|
||||||
|
|
|
@ -441,6 +441,17 @@ class VotesFactory(Factory):
|
||||||
object_id = factory.Sequence(lambda n: n)
|
object_id = factory.Sequence(lambda n: n)
|
||||||
|
|
||||||
|
|
||||||
|
class WatchedFactory(Factory):
|
||||||
|
class Meta:
|
||||||
|
model = "notifications.Watched"
|
||||||
|
strategy = factory.CREATE_STRATEGY
|
||||||
|
|
||||||
|
content_type = factory.SubFactory("tests.factories.ContentTypeFactory")
|
||||||
|
object_id = factory.Sequence(lambda n: n)
|
||||||
|
user = factory.SubFactory("tests.factories.UserFactory")
|
||||||
|
project = factory.SubFactory("tests.factories.ProjectFactory")
|
||||||
|
|
||||||
|
|
||||||
class ContentTypeFactory(Factory):
|
class ContentTypeFactory(Factory):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = "contenttypes.ContentType"
|
model = "contenttypes.ContentType"
|
||||||
|
|
|
@ -9,6 +9,7 @@ from taiga.base.utils import json
|
||||||
from tests import factories as f
|
from tests import factories as f
|
||||||
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
||||||
from taiga.projects.votes.services import add_vote
|
from taiga.projects.votes.services import add_vote
|
||||||
|
from taiga.projects.notifications.services import add_watcher
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
@ -616,3 +617,51 @@ def test_issue_action_unwatch(client, data):
|
||||||
assert results == [401, 200, 200, 200, 200]
|
assert results == [401, 200, 200, 200, 200]
|
||||||
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
assert results == [404, 404, 404, 200, 200]
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_issue_watchers_list(client, data):
|
||||||
|
public_url = reverse('issue-watchers-list', kwargs={"resource_id": data.public_issue.pk})
|
||||||
|
private_url1 = reverse('issue-watchers-list', kwargs={"resource_id": data.private_issue1.pk})
|
||||||
|
private_url2 = reverse('issue-watchers-list', kwargs={"resource_id": data.private_issue2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_issue_watchers_retrieve(client, data):
|
||||||
|
add_watcher(data.public_issue, data.project_owner)
|
||||||
|
public_url = reverse('issue-watchers-detail', kwargs={"resource_id": data.public_issue.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_issue1, data.project_owner)
|
||||||
|
private_url1 = reverse('issue-watchers-detail', kwargs={"resource_id": data.private_issue1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_issue2, data.project_owner)
|
||||||
|
private_url2 = reverse('issue-watchers-detail', kwargs={"resource_id": data.private_issue2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.core.urlresolvers import reverse
|
||||||
from taiga.base.utils import json
|
from taiga.base.utils import json
|
||||||
from taiga.projects.milestones.serializers import MilestoneSerializer
|
from taiga.projects.milestones.serializers import MilestoneSerializer
|
||||||
from taiga.projects.milestones.models import Milestone
|
from taiga.projects.milestones.models import Milestone
|
||||||
|
from taiga.projects.notifications.services import add_watcher
|
||||||
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
|
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
|
||||||
|
|
||||||
from tests import factories as f
|
from tests import factories as f
|
||||||
|
@ -274,3 +275,93 @@ def test_milestone_action_stats(client, data):
|
||||||
|
|
||||||
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
assert results == [401, 403, 403, 200, 200]
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_milestone_action_watch(client, data):
|
||||||
|
public_url = reverse('milestones-watch', kwargs={"pk": data.public_milestone.pk})
|
||||||
|
private_url1 = reverse('milestones-watch', kwargs={"pk": data.private_milestone1.pk})
|
||||||
|
private_url2 = reverse('milestones-watch', kwargs={"pk": data.private_milestone2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'post', public_url, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url1, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_milestone_action_unwatch(client, data):
|
||||||
|
public_url = reverse('milestones-unwatch', kwargs={"pk": data.public_milestone.pk})
|
||||||
|
private_url1 = reverse('milestones-unwatch', kwargs={"pk": data.private_milestone1.pk})
|
||||||
|
private_url2 = reverse('milestones-unwatch', kwargs={"pk": data.private_milestone2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'post', public_url, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url1, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_milestone_watchers_list(client, data):
|
||||||
|
public_url = reverse('milestone-watchers-list', kwargs={"resource_id": data.public_milestone.pk})
|
||||||
|
private_url1 = reverse('milestone-watchers-list', kwargs={"resource_id": data.private_milestone1.pk})
|
||||||
|
private_url2 = reverse('milestone-watchers-list', kwargs={"resource_id": data.private_milestone2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_milestone_watchers_retrieve(client, data):
|
||||||
|
add_watcher(data.public_milestone, data.project_owner)
|
||||||
|
public_url = reverse('milestone-watchers-detail', kwargs={"resource_id": data.public_milestone.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_milestone1, data.project_owner)
|
||||||
|
private_url1 = reverse('milestone-watchers-detail', kwargs={"resource_id": data.private_milestone1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_milestone2, data.project_owner)
|
||||||
|
private_url2 = reverse('milestone-watchers-detail', kwargs={"resource_id": data.private_milestone2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
|
@ -81,6 +81,13 @@ def data():
|
||||||
f.VotesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2)
|
f.VotesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2)
|
||||||
f.VotesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2)
|
f.VotesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2)
|
||||||
|
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_member_with_perms)
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_owner)
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_member_with_perms)
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_owner)
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_member_with_perms)
|
||||||
|
f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_owner)
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,6 +116,7 @@ def test_project_update(client, data):
|
||||||
|
|
||||||
project_data = ProjectDetailSerializer(data.private_project2).data
|
project_data = ProjectDetailSerializer(data.private_project2).data
|
||||||
project_data["is_private"] = False
|
project_data["is_private"] = False
|
||||||
|
|
||||||
project_data = json.dumps(project_data)
|
project_data = json.dumps(project_data)
|
||||||
|
|
||||||
users = [
|
users = [
|
||||||
|
@ -300,6 +308,51 @@ def test_project_fans_retrieve(client, data):
|
||||||
assert results == [401, 403, 403, 200, 200]
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_project_watchers_list(client, data):
|
||||||
|
public_url = reverse('project-watchers-list', kwargs={"resource_id": data.public_project.pk})
|
||||||
|
private1_url = reverse('project-watchers-list', kwargs={"resource_id": data.private_project1.pk})
|
||||||
|
private2_url = reverse('project-watchers-list', kwargs={"resource_id": data.private_project2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method_and_count(client, 'get', public_url, None, users)
|
||||||
|
assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)]
|
||||||
|
results = helper_test_http_method_and_count(client, 'get', private1_url, None, users)
|
||||||
|
assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)]
|
||||||
|
results = helper_test_http_method_and_count(client, 'get', private2_url, None, users)
|
||||||
|
assert results == [(401, 0), (403, 0), (403, 0), (200, 2), (200, 2)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_project_watchers_retrieve(client, data):
|
||||||
|
public_url = reverse('project-watchers-detail', kwargs={"resource_id": data.public_project.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
private1_url = reverse('project-watchers-detail', kwargs={"resource_id": data.private_project1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
private2_url = reverse('project-watchers-detail', kwargs={"resource_id": data.private_project2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private1_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private2_url, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
def test_project_action_create_template(client, data):
|
def test_project_action_create_template(client, data):
|
||||||
public_url = reverse('projects-create-template', kwargs={"pk": data.public_project.pk})
|
public_url = reverse('projects-create-template', kwargs={"pk": data.public_project.pk})
|
||||||
private1_url = reverse('projects-create-template', kwargs={"pk": data.private_project1.pk})
|
private1_url = reverse('projects-create-template', kwargs={"pk": data.private_project1.pk})
|
||||||
|
|
|
@ -10,6 +10,7 @@ from taiga.projects.occ import OCCResourceMixin
|
||||||
from tests import factories as f
|
from tests import factories as f
|
||||||
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
||||||
from taiga.projects.votes.services import add_vote
|
from taiga.projects.votes.services import add_vote
|
||||||
|
from taiga.projects.notifications.services import add_watcher
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
@ -571,3 +572,51 @@ def test_task_action_unwatch(client, data):
|
||||||
assert results == [401, 200, 200, 200, 200]
|
assert results == [401, 200, 200, 200, 200]
|
||||||
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
assert results == [404, 404, 404, 200, 200]
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_task_watchers_list(client, data):
|
||||||
|
public_url = reverse('task-watchers-list', kwargs={"resource_id": data.public_task.pk})
|
||||||
|
private_url1 = reverse('task-watchers-list', kwargs={"resource_id": data.private_task1.pk})
|
||||||
|
private_url2 = reverse('task-watchers-list', kwargs={"resource_id": data.private_task2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_task_watchers_retrieve(client, data):
|
||||||
|
add_watcher(data.public_task, data.project_owner)
|
||||||
|
public_url = reverse('task-watchers-detail', kwargs={"resource_id": data.public_task.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_task1, data.project_owner)
|
||||||
|
private_url1 = reverse('task-watchers-detail', kwargs={"resource_id": data.private_task1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_task2, data.project_owner)
|
||||||
|
private_url2 = reverse('task-watchers-detail', kwargs={"resource_id": data.private_task2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
|
@ -10,6 +10,7 @@ from taiga.projects.occ import OCCResourceMixin
|
||||||
from tests import factories as f
|
from tests import factories as f
|
||||||
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
||||||
from taiga.projects.votes.services import add_vote
|
from taiga.projects.votes.services import add_vote
|
||||||
|
from taiga.projects.notifications.services import add_watcher
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
@ -570,3 +571,51 @@ def test_user_story_action_unwatch(client, data):
|
||||||
assert results == [401, 200, 200, 200, 200]
|
assert results == [401, 200, 200, 200, 200]
|
||||||
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
assert results == [404, 404, 404, 200, 200]
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_userstory_watchers_list(client, data):
|
||||||
|
public_url = reverse('userstory-watchers-list', kwargs={"resource_id": data.public_user_story.pk})
|
||||||
|
private_url1 = reverse('userstory-watchers-list', kwargs={"resource_id": data.private_user_story1.pk})
|
||||||
|
private_url2 = reverse('userstory-watchers-list', kwargs={"resource_id": data.private_user_story2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_userstory_watchers_retrieve(client, data):
|
||||||
|
add_watcher(data.public_user_story, data.project_owner)
|
||||||
|
public_url = reverse('userstory-watchers-detail', kwargs={"resource_id": data.public_user_story.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_user_story1, data.project_owner)
|
||||||
|
private_url1 = reverse('userstory-watchers-detail', kwargs={"resource_id": data.private_user_story1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_user_story2, data.project_owner)
|
||||||
|
private_url2 = reverse('userstory-watchers-detail', kwargs={"resource_id": data.private_user_story2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from taiga.base.utils import json
|
from taiga.base.utils import json
|
||||||
|
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
|
||||||
|
from taiga.projects.notifications.services import add_watcher
|
||||||
|
from taiga.projects.occ import OCCResourceMixin
|
||||||
from taiga.projects.wiki.serializers import WikiPageSerializer, WikiLinkSerializer
|
from taiga.projects.wiki.serializers import WikiPageSerializer, WikiLinkSerializer
|
||||||
from taiga.projects.wiki.models import WikiPage, WikiLink
|
from taiga.projects.wiki.models import WikiPage, WikiLink
|
||||||
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
|
|
||||||
from taiga.projects.occ import OCCResourceMixin
|
|
||||||
|
|
||||||
from tests import factories as f
|
from tests import factories as f
|
||||||
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
|
||||||
|
@ -436,3 +437,93 @@ def test_wiki_link_patch(client, data):
|
||||||
patch_data = json.dumps({"title": "test"})
|
patch_data = json.dumps({"title": "test"})
|
||||||
results = helper_test_http_method(client, 'patch', private_url2, patch_data, users)
|
results = helper_test_http_method(client, 'patch', private_url2, patch_data, users)
|
||||||
assert results == [401, 403, 403, 200, 200]
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wikipage_action_watch(client, data):
|
||||||
|
public_url = reverse('wiki-watch', kwargs={"pk": data.public_wiki_page.pk})
|
||||||
|
private_url1 = reverse('wiki-watch', kwargs={"pk": data.private_wiki_page1.pk})
|
||||||
|
private_url2 = reverse('wiki-watch', kwargs={"pk": data.private_wiki_page2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'post', public_url, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url1, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wikipage_action_unwatch(client, data):
|
||||||
|
public_url = reverse('wiki-unwatch', kwargs={"pk": data.public_wiki_page.pk})
|
||||||
|
private_url1 = reverse('wiki-unwatch', kwargs={"pk": data.private_wiki_page1.pk})
|
||||||
|
private_url2 = reverse('wiki-unwatch', kwargs={"pk": data.private_wiki_page2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'post', public_url, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url1, "", users)
|
||||||
|
assert results == [401, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'post', private_url2, "", users)
|
||||||
|
assert results == [404, 404, 404, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wikipage_watchers_list(client, data):
|
||||||
|
public_url = reverse('wiki-watchers-list', kwargs={"resource_id": data.public_wiki_page.pk})
|
||||||
|
private_url1 = reverse('wiki-watchers-list', kwargs={"resource_id": data.private_wiki_page1.pk})
|
||||||
|
private_url2 = reverse('wiki-watchers-list', kwargs={"resource_id": data.private_wiki_page2.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wikipage_watchers_retrieve(client, data):
|
||||||
|
add_watcher(data.public_wiki_page, data.project_owner)
|
||||||
|
public_url = reverse('wiki-watchers-detail', kwargs={"resource_id": data.public_wiki_page.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_wiki_page1, data.project_owner)
|
||||||
|
private_url1 = reverse('wiki-watchers-detail', kwargs={"resource_id": data.private_wiki_page1.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
add_watcher(data.private_wiki_page2, data.project_owner)
|
||||||
|
private_url2 = reverse('wiki-watchers-detail', kwargs={"resource_id": data.private_wiki_page2.pk,
|
||||||
|
"pk": data.project_owner.pk})
|
||||||
|
|
||||||
|
users = [
|
||||||
|
None,
|
||||||
|
data.registered_user,
|
||||||
|
data.project_member_without_perms,
|
||||||
|
data.project_member_with_perms,
|
||||||
|
data.project_owner
|
||||||
|
]
|
||||||
|
|
||||||
|
results = helper_test_http_method(client, 'get', public_url, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url1, None, users)
|
||||||
|
assert results == [200, 200, 200, 200, 200]
|
||||||
|
results = helper_test_http_method(client, 'get', private_url2, None, users)
|
||||||
|
assert results == [401, 403, 403, 200, 200]
|
||||||
|
|
|
@ -412,6 +412,6 @@ def test_custom_fields_csv_generation():
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
reader = csv.reader(data)
|
reader = csv.reader(data)
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[16] == attr.name
|
assert row[18] == attr.name
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[16] == "val1"
|
assert row[18] == "val1"
|
||||||
|
|
|
@ -211,6 +211,124 @@ def test_users_to_notify():
|
||||||
assert users == {member1.user, issue.get_owner()}
|
assert users == {member1.user, issue.get_owner()}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_1():
|
||||||
|
# If:
|
||||||
|
# - the user is watching the issue
|
||||||
|
# - the user is not watching the project
|
||||||
|
# - the notify policy is watch
|
||||||
|
# Then:
|
||||||
|
# - email is sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
issue.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.watch
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {watching_user, issue.owner}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_2():
|
||||||
|
# If:
|
||||||
|
# - the user is watching the issue
|
||||||
|
# - the user is not watching the project
|
||||||
|
# - the notify policy is notwatch
|
||||||
|
# Then:
|
||||||
|
# - email is sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
issue.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.notwatch
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {watching_user, issue.owner}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_3():
|
||||||
|
# If:
|
||||||
|
# - the user is watching the issue
|
||||||
|
# - the user is not watching the project
|
||||||
|
# - the notify policy is ignore
|
||||||
|
# Then:
|
||||||
|
# - email is not sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
issue.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.ignore
|
||||||
|
watching_user_policy.save()
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {issue.owner}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_4():
|
||||||
|
# If:
|
||||||
|
# - the user is not watching the issue
|
||||||
|
# - the user is watching the project
|
||||||
|
# - the notify policy is ignore
|
||||||
|
# Then:
|
||||||
|
# - email is not sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
project.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.ignore
|
||||||
|
watching_user_policy.save()
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {issue.owner}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_5():
|
||||||
|
# If:
|
||||||
|
# - the user is not watching the issue
|
||||||
|
# - the user is watching the project
|
||||||
|
# - the notify policy is watch
|
||||||
|
# Then:
|
||||||
|
# - email is sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
project.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.watch
|
||||||
|
watching_user_policy.save()
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {watching_user, issue.owner}
|
||||||
|
|
||||||
|
|
||||||
|
def test_watching_users_to_notify_on_issue_modification_6():
|
||||||
|
# If:
|
||||||
|
# - the user is not watching the issue
|
||||||
|
# - the user is watching the project
|
||||||
|
# - the notify policy is notwatch
|
||||||
|
# Then:
|
||||||
|
# - email is sent
|
||||||
|
project = f.ProjectFactory.create(anon_permissions= ["view_issues"])
|
||||||
|
issue = f.IssueFactory.create(project=project)
|
||||||
|
watching_user = f.UserFactory()
|
||||||
|
project.add_watcher(watching_user)
|
||||||
|
watching_user_policy = services.get_notify_policy(project, watching_user)
|
||||||
|
issue.description = "test1"
|
||||||
|
issue.save()
|
||||||
|
watching_user_policy.notify_level = NotifyLevel.notwatch
|
||||||
|
watching_user_policy.save()
|
||||||
|
users = services.get_users_to_notify(issue)
|
||||||
|
assert users == {watching_user, issue.owner}
|
||||||
|
|
||||||
|
|
||||||
def test_send_notifications_using_services_method(settings, mail):
|
def test_send_notifications_using_services_method(settings, mail):
|
||||||
settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1
|
settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,6 @@ def test_custom_fields_csv_generation():
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
reader = csv.reader(data)
|
reader = csv.reader(data)
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[17] == attr.name
|
assert row[19] == attr.name
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[17] == "val1"
|
assert row[19] == "val1"
|
||||||
|
|
|
@ -196,8 +196,10 @@ def test_create_membership_timeline():
|
||||||
|
|
||||||
|
|
||||||
def test_update_project_timeline():
|
def test_update_project_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
project = factories.ProjectFactory.create(name="test project timeline")
|
project = factories.ProjectFactory.create(name="test project timeline")
|
||||||
history_services.take_snapshot(project, user=project.owner)
|
history_services.take_snapshot(project, user=project.owner)
|
||||||
|
project.add_watcher(user_watcher)
|
||||||
project.name = "test project timeline updated"
|
project.name = "test project timeline updated"
|
||||||
project.save()
|
project.save()
|
||||||
history_services.take_snapshot(project, user=project.owner)
|
history_services.take_snapshot(project, user=project.owner)
|
||||||
|
@ -206,11 +208,18 @@ def test_update_project_timeline():
|
||||||
assert project_timeline[0].data["project"]["name"] == "test project timeline updated"
|
assert project_timeline[0].data["project"]["name"] == "test project timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["name"][0] == "test project timeline"
|
assert project_timeline[0].data["values_diff"]["name"][0] == "test project timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["name"][1] == "test project timeline updated"
|
assert project_timeline[0].data["values_diff"]["name"][1] == "test project timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "projects.project.change"
|
||||||
|
assert user_watcher_timeline[0].data["project"]["name"] == "test project timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["name"][0] == "test project timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["name"][1] == "test project timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_milestone_timeline():
|
def test_update_milestone_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
milestone = factories.MilestoneFactory.create(name="test milestone timeline")
|
milestone = factories.MilestoneFactory.create(name="test milestone timeline")
|
||||||
history_services.take_snapshot(milestone, user=milestone.owner)
|
history_services.take_snapshot(milestone, user=milestone.owner)
|
||||||
|
milestone.add_watcher(user_watcher)
|
||||||
milestone.name = "test milestone timeline updated"
|
milestone.name = "test milestone timeline updated"
|
||||||
milestone.save()
|
milestone.save()
|
||||||
history_services.take_snapshot(milestone, user=milestone.owner)
|
history_services.take_snapshot(milestone, user=milestone.owner)
|
||||||
|
@ -219,11 +228,18 @@ def test_update_milestone_timeline():
|
||||||
assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline updated"
|
assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["name"][0] == "test milestone timeline"
|
assert project_timeline[0].data["values_diff"]["name"][0] == "test milestone timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["name"][1] == "test milestone timeline updated"
|
assert project_timeline[0].data["values_diff"]["name"][1] == "test milestone timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "milestones.milestone.change"
|
||||||
|
assert user_watcher_timeline[0].data["milestone"]["name"] == "test milestone timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["name"][0] == "test milestone timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["name"][1] == "test milestone timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_user_story_timeline():
|
def test_update_user_story_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner)
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
|
user_story.add_watcher(user_watcher)
|
||||||
user_story.subject = "test us timeline updated"
|
user_story.subject = "test us timeline updated"
|
||||||
user_story.save()
|
user_story.save()
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner)
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
|
@ -232,11 +248,18 @@ def test_update_user_story_timeline():
|
||||||
assert project_timeline[0].data["userstory"]["subject"] == "test us timeline updated"
|
assert project_timeline[0].data["userstory"]["subject"] == "test us timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][0] == "test us timeline"
|
assert project_timeline[0].data["values_diff"]["subject"][0] == "test us timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][1] == "test us timeline updated"
|
assert project_timeline[0].data["values_diff"]["subject"][1] == "test us timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "userstories.userstory.change"
|
||||||
|
assert user_watcher_timeline[0].data["userstory"]["subject"] == "test us timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test us timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test us timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_issue_timeline():
|
def test_update_issue_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
issue = factories.IssueFactory.create(subject="test issue timeline")
|
issue = factories.IssueFactory.create(subject="test issue timeline")
|
||||||
history_services.take_snapshot(issue, user=issue.owner)
|
history_services.take_snapshot(issue, user=issue.owner)
|
||||||
|
issue.add_watcher(user_watcher)
|
||||||
issue.subject = "test issue timeline updated"
|
issue.subject = "test issue timeline updated"
|
||||||
issue.save()
|
issue.save()
|
||||||
history_services.take_snapshot(issue, user=issue.owner)
|
history_services.take_snapshot(issue, user=issue.owner)
|
||||||
|
@ -245,11 +268,18 @@ def test_update_issue_timeline():
|
||||||
assert project_timeline[0].data["issue"]["subject"] == "test issue timeline updated"
|
assert project_timeline[0].data["issue"]["subject"] == "test issue timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][0] == "test issue timeline"
|
assert project_timeline[0].data["values_diff"]["subject"][0] == "test issue timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][1] == "test issue timeline updated"
|
assert project_timeline[0].data["values_diff"]["subject"][1] == "test issue timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "issues.issue.change"
|
||||||
|
assert user_watcher_timeline[0].data["issue"]["subject"] == "test issue timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test issue timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test issue timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_task_timeline():
|
def test_update_task_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
task = factories.TaskFactory.create(subject="test task timeline")
|
task = factories.TaskFactory.create(subject="test task timeline")
|
||||||
history_services.take_snapshot(task, user=task.owner)
|
history_services.take_snapshot(task, user=task.owner)
|
||||||
|
task.add_watcher(user_watcher)
|
||||||
task.subject = "test task timeline updated"
|
task.subject = "test task timeline updated"
|
||||||
task.save()
|
task.save()
|
||||||
history_services.take_snapshot(task, user=task.owner)
|
history_services.take_snapshot(task, user=task.owner)
|
||||||
|
@ -258,11 +288,18 @@ def test_update_task_timeline():
|
||||||
assert project_timeline[0].data["task"]["subject"] == "test task timeline updated"
|
assert project_timeline[0].data["task"]["subject"] == "test task timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][0] == "test task timeline"
|
assert project_timeline[0].data["values_diff"]["subject"][0] == "test task timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["subject"][1] == "test task timeline updated"
|
assert project_timeline[0].data["values_diff"]["subject"][1] == "test task timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "tasks.task.change"
|
||||||
|
assert user_watcher_timeline[0].data["task"]["subject"] == "test task timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test task timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test task timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_wiki_page_timeline():
|
def test_update_wiki_page_timeline():
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
page = factories.WikiPageFactory.create(slug="test wiki page timeline")
|
page = factories.WikiPageFactory.create(slug="test wiki page timeline")
|
||||||
history_services.take_snapshot(page, user=page.owner)
|
history_services.take_snapshot(page, user=page.owner)
|
||||||
|
page.add_watcher(user_watcher)
|
||||||
page.slug = "test wiki page timeline updated"
|
page.slug = "test wiki page timeline updated"
|
||||||
page.save()
|
page.save()
|
||||||
history_services.take_snapshot(page, user=page.owner)
|
history_services.take_snapshot(page, user=page.owner)
|
||||||
|
@ -271,6 +308,11 @@ def test_update_wiki_page_timeline():
|
||||||
assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline updated"
|
assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline updated"
|
||||||
assert project_timeline[0].data["values_diff"]["slug"][0] == "test wiki page timeline"
|
assert project_timeline[0].data["values_diff"]["slug"][0] == "test wiki page timeline"
|
||||||
assert project_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated"
|
assert project_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "wiki.wikipage.change"
|
||||||
|
assert user_watcher_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline updated"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["slug"][0] == "test wiki page timeline"
|
||||||
|
assert user_watcher_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated"
|
||||||
|
|
||||||
|
|
||||||
def test_update_membership_timeline():
|
def test_update_membership_timeline():
|
||||||
|
@ -298,50 +340,80 @@ def test_update_membership_timeline():
|
||||||
|
|
||||||
def test_delete_project_timeline():
|
def test_delete_project_timeline():
|
||||||
project = factories.ProjectFactory.create(name="test project timeline")
|
project = factories.ProjectFactory.create(name="test project timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
project.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(project, user=project.owner, delete=True)
|
history_services.take_snapshot(project, user=project.owner, delete=True)
|
||||||
user_timeline = service.get_project_timeline(project)
|
user_timeline = service.get_project_timeline(project)
|
||||||
assert user_timeline[0].event_type == "projects.project.delete"
|
assert user_timeline[0].event_type == "projects.project.delete"
|
||||||
assert user_timeline[0].data["project"]["id"] == project.id
|
assert user_timeline[0].data["project"]["id"] == project.id
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "projects.project.delete"
|
||||||
|
assert user_watcher_timeline[0].data["project"]["id"] == project.id
|
||||||
|
|
||||||
|
|
||||||
def test_delete_milestone_timeline():
|
def test_delete_milestone_timeline():
|
||||||
milestone = factories.MilestoneFactory.create(name="test milestone timeline")
|
milestone = factories.MilestoneFactory.create(name="test milestone timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
milestone.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(milestone, user=milestone.owner, delete=True)
|
history_services.take_snapshot(milestone, user=milestone.owner, delete=True)
|
||||||
project_timeline = service.get_project_timeline(milestone.project)
|
project_timeline = service.get_project_timeline(milestone.project)
|
||||||
assert project_timeline[0].event_type == "milestones.milestone.delete"
|
assert project_timeline[0].event_type == "milestones.milestone.delete"
|
||||||
assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline"
|
assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "milestones.milestone.delete"
|
||||||
|
assert user_watcher_timeline[0].data["milestone"]["name"] == "test milestone timeline"
|
||||||
|
|
||||||
|
|
||||||
def test_delete_user_story_timeline():
|
def test_delete_user_story_timeline():
|
||||||
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
user_story.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner, delete=True)
|
history_services.take_snapshot(user_story, user=user_story.owner, delete=True)
|
||||||
project_timeline = service.get_project_timeline(user_story.project)
|
project_timeline = service.get_project_timeline(user_story.project)
|
||||||
assert project_timeline[0].event_type == "userstories.userstory.delete"
|
assert project_timeline[0].event_type == "userstories.userstory.delete"
|
||||||
assert project_timeline[0].data["userstory"]["subject"] == "test us timeline"
|
assert project_timeline[0].data["userstory"]["subject"] == "test us timeline"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "userstories.userstory.delete"
|
||||||
|
assert user_watcher_timeline[0].data["userstory"]["subject"] == "test us timeline"
|
||||||
|
|
||||||
|
|
||||||
def test_delete_issue_timeline():
|
def test_delete_issue_timeline():
|
||||||
issue = factories.IssueFactory.create(subject="test issue timeline")
|
issue = factories.IssueFactory.create(subject="test issue timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
issue.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(issue, user=issue.owner, delete=True)
|
history_services.take_snapshot(issue, user=issue.owner, delete=True)
|
||||||
project_timeline = service.get_project_timeline(issue.project)
|
project_timeline = service.get_project_timeline(issue.project)
|
||||||
assert project_timeline[0].event_type == "issues.issue.delete"
|
assert project_timeline[0].event_type == "issues.issue.delete"
|
||||||
assert project_timeline[0].data["issue"]["subject"] == "test issue timeline"
|
assert project_timeline[0].data["issue"]["subject"] == "test issue timeline"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "issues.issue.delete"
|
||||||
|
assert user_watcher_timeline[0].data["issue"]["subject"] == "test issue timeline"
|
||||||
|
|
||||||
|
|
||||||
def test_delete_task_timeline():
|
def test_delete_task_timeline():
|
||||||
task = factories.TaskFactory.create(subject="test task timeline")
|
task = factories.TaskFactory.create(subject="test task timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
task.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(task, user=task.owner, delete=True)
|
history_services.take_snapshot(task, user=task.owner, delete=True)
|
||||||
project_timeline = service.get_project_timeline(task.project)
|
project_timeline = service.get_project_timeline(task.project)
|
||||||
assert project_timeline[0].event_type == "tasks.task.delete"
|
assert project_timeline[0].event_type == "tasks.task.delete"
|
||||||
assert project_timeline[0].data["task"]["subject"] == "test task timeline"
|
assert project_timeline[0].data["task"]["subject"] == "test task timeline"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "tasks.task.delete"
|
||||||
|
assert user_watcher_timeline[0].data["task"]["subject"] == "test task timeline"
|
||||||
|
|
||||||
|
|
||||||
def test_delete_wiki_page_timeline():
|
def test_delete_wiki_page_timeline():
|
||||||
page = factories.WikiPageFactory.create(slug="test wiki page timeline")
|
page = factories.WikiPageFactory.create(slug="test wiki page timeline")
|
||||||
|
user_watcher= factories.UserFactory()
|
||||||
|
page.add_watcher(user_watcher)
|
||||||
history_services.take_snapshot(page, user=page.owner, delete=True)
|
history_services.take_snapshot(page, user=page.owner, delete=True)
|
||||||
project_timeline = service.get_project_timeline(page.project)
|
project_timeline = service.get_project_timeline(page.project)
|
||||||
assert project_timeline[0].event_type == "wiki.wikipage.delete"
|
assert project_timeline[0].event_type == "wiki.wikipage.delete"
|
||||||
assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline"
|
assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline"
|
||||||
|
user_watcher_timeline = service.get_profile_timeline(user_watcher)
|
||||||
|
assert user_watcher_timeline[0].event_type == "wiki.wikipage.delete"
|
||||||
|
assert user_watcher_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline"
|
||||||
|
|
||||||
|
|
||||||
def test_delete_membership_timeline():
|
def test_delete_membership_timeline():
|
||||||
|
|
|
@ -483,6 +483,6 @@ def test_custom_fields_csv_generation():
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
reader = csv.reader(data)
|
reader = csv.reader(data)
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[24] == attr.name
|
assert row[26] == attr.name
|
||||||
row = next(reader)
|
row = next(reader)
|
||||||
assert row[24] == "val1"
|
assert row[26] == "val1"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import json
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
@ -45,3 +46,78 @@ def test_unwatch_issue(client):
|
||||||
response = client.post(url)
|
response = client.post(url)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_issue_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
issue = f.IssueFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
|
||||||
|
f.WatchedFactory.create(content_object=issue, user=user)
|
||||||
|
url = reverse("issue-watchers-list", args=(issue.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data[0]['id'] == user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_issue_watcher(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
issue = f.IssueFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
|
||||||
|
watch = f.WatchedFactory.create(content_object=issue, user=user)
|
||||||
|
url = reverse("issue-watchers-detail", args=(issue.id, watch.user.id))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['id'] == watch.user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_issue_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
issue = f.IssueFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
|
||||||
|
url = reverse("issues-detail", args=(issue.id,))
|
||||||
|
|
||||||
|
f.WatchedFactory.create(content_object=issue, user=user)
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_issue_is_watched(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
issue = f.IssueFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
|
||||||
|
url_detail = reverse("issues-detail", args=(issue.id,))
|
||||||
|
url_watch = reverse("issues-watch", args=(issue.id,))
|
||||||
|
url_unwatch = reverse("issues-unwatch", args=(issue.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
||||||
|
response = client.post(url_watch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
assert response.data['is_watched'] == True
|
||||||
|
|
||||||
|
response = client.post(url_unwatch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import json
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
@ -45,3 +46,78 @@ def test_unwacth_project(client):
|
||||||
response = client.post(url)
|
response = client.post(url)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_project_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.create_project(owner=user)
|
||||||
|
f.MembershipFactory.create(project=project, user=user, is_owner=True)
|
||||||
|
f.WatchedFactory.create(content_object=project, user=user)
|
||||||
|
url = reverse("project-watchers-list", args=(project.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data[0]['id'] == user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_project_watcher(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.create_project(owner=user)
|
||||||
|
f.MembershipFactory.create(project=project, user=user, is_owner=True)
|
||||||
|
watch = f.WatchedFactory.create(content_object=project, user=user)
|
||||||
|
url = reverse("project-watchers-detail", args=(project.id, watch.user.id))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['id'] == watch.user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_project_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.create_project(owner=user)
|
||||||
|
f.MembershipFactory.create(project=project, user=user, is_owner=True)
|
||||||
|
url = reverse("projects-detail", args=(project.id,))
|
||||||
|
|
||||||
|
f.WatchedFactory.create(content_object=project, user=user)
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_project_is_watched(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.create_project(owner=user)
|
||||||
|
f.MembershipFactory.create(project=project, user=user, is_owner=True)
|
||||||
|
url_detail = reverse("projects-detail", args=(project.id,))
|
||||||
|
url_watch = reverse("projects-watch", args=(project.id,))
|
||||||
|
url_unwatch = reverse("projects-unwatch", args=(project.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
||||||
|
response = client.post(url_watch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
assert response.data['is_watched'] == True
|
||||||
|
|
||||||
|
response = client.post(url_unwatch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import json
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
@ -45,3 +46,78 @@ def test_unwatch_task(client):
|
||||||
response = client.post(url)
|
response = client.post(url)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_task_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
task = f.TaskFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=task.project, user=user, is_owner=True)
|
||||||
|
f.WatchedFactory.create(content_object=task, user=user)
|
||||||
|
url = reverse("task-watchers-list", args=(task.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data[0]['id'] == user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_task_watcher(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
task = f.TaskFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=task.project, user=user, is_owner=True)
|
||||||
|
watch = f.WatchedFactory.create(content_object=task, user=user)
|
||||||
|
url = reverse("task-watchers-detail", args=(task.id, watch.user.id))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['id'] == watch.user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_task_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
task = f.TaskFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=task.project, user=user, is_owner=True)
|
||||||
|
url = reverse("tasks-detail", args=(task.id,))
|
||||||
|
|
||||||
|
f.WatchedFactory.create(content_object=task, user=user)
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_task_is_watched(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
task = f.TaskFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=task.project, user=user, is_owner=True)
|
||||||
|
url_detail = reverse("tasks-detail", args=(task.id,))
|
||||||
|
url_watch = reverse("tasks-watch", args=(task.id,))
|
||||||
|
url_unwatch = reverse("tasks-unwatch", args=(task.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
||||||
|
response = client.post(url_watch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
assert response.data['is_watched'] == True
|
||||||
|
|
||||||
|
response = client.post(url_unwatch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import json
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
@ -45,3 +46,78 @@ def test_unwatch_user_story(client):
|
||||||
response = client.post(url)
|
response = client.post(url)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_user_story_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
user_story = f.UserStoryFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True)
|
||||||
|
f.WatchedFactory.create(content_object=user_story, user=user)
|
||||||
|
url = reverse("userstory-watchers-list", args=(user_story.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data[0]['id'] == user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_user_story_watcher(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
user_story = f.UserStoryFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True)
|
||||||
|
watch = f.WatchedFactory.create(content_object=user_story, user=user)
|
||||||
|
url = reverse("userstory-watchers-detail", args=(user_story.id, watch.user.id))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['id'] == watch.user.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_user_story_watchers(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
user_story = f.UserStoryFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True)
|
||||||
|
url = reverse("userstories-detail", args=(user_story.id,))
|
||||||
|
|
||||||
|
f.WatchedFactory.create(content_object=user_story, user=user)
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_user_story_is_watched(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
user_story = f.UserStoryFactory(owner=user)
|
||||||
|
f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True)
|
||||||
|
url_detail = reverse("userstories-detail", args=(user_story.id,))
|
||||||
|
url_watch = reverse("userstories-watch", args=(user_story.id,))
|
||||||
|
url_unwatch = reverse("userstories-unwatch", args=(user_story.id,))
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
||||||
|
response = client.post(url_watch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == [user.id]
|
||||||
|
assert response.data['is_watched'] == True
|
||||||
|
|
||||||
|
response = client.post(url_unwatch)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = client.get(url_detail)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data['watchers'] == []
|
||||||
|
assert response.data['is_watched'] == False
|
||||||
|
|
|
@ -90,3 +90,26 @@ def test_new_object_with_two_webhook(settings):
|
||||||
with patch('taiga.webhooks.tasks.delete_webhook') as delete_webhook_mock:
|
with patch('taiga.webhooks.tasks.delete_webhook') as delete_webhook_mock:
|
||||||
services.take_snapshot(obj, user=obj.owner, comment="test", delete=True)
|
services.take_snapshot(obj, user=obj.owner, comment="test", delete=True)
|
||||||
assert delete_webhook_mock.call_count == 2
|
assert delete_webhook_mock.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_send_request_one_webhook(settings):
|
||||||
|
settings.WEBHOOKS_ENABLED = True
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.WebhookFactory.create(project=project)
|
||||||
|
|
||||||
|
objects = [
|
||||||
|
f.IssueFactory.create(project=project),
|
||||||
|
f.TaskFactory.create(project=project),
|
||||||
|
f.UserStoryFactory.create(project=project),
|
||||||
|
f.WikiPageFactory.create(project=project)
|
||||||
|
]
|
||||||
|
|
||||||
|
for obj in objects:
|
||||||
|
with patch('taiga.webhooks.tasks._send_request') as _send_request_mock:
|
||||||
|
services.take_snapshot(obj, user=obj.owner, comment="test")
|
||||||
|
assert _send_request_mock.call_count == 1
|
||||||
|
|
||||||
|
for obj in objects:
|
||||||
|
with patch('taiga.webhooks.tasks._send_request') as _send_request_mock:
|
||||||
|
services.take_snapshot(obj, user=obj.owner, comment="test", delete=True)
|
||||||
|
assert _send_request_mock.call_count == 1
|
||||||
|
|
Loading…
Reference in New Issue