Fix some errors related to watched and likes lists

remotes/origin/logger
Alejandro Alonso 2015-09-09 15:32:18 +02:00 committed by David Barragán Merino
parent 0d21f04a87
commit bf57ace9a2
6 changed files with 181 additions and 61 deletions

View File

@ -189,14 +189,15 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer):
#watchers is not a field from the model but can be attached in the get_queryset of the viewset. #watchers is not a field from the model but can be attached in the get_queryset of the viewset.
#If that's the case we need to remove it before calling the super method #If that's the case we need to remove it before calling the super method
watcher_field = self.fields.pop("watchers", None) watcher_field = self.fields.pop("watchers", None)
instance = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) self.validate_watchers(attrs, "watchers")
if instance is not None and self.validate_watchers(attrs, "watchers"): new_watcher_ids = set(attrs.pop("watchers", []))
#A partial update can exclude the watchers field obj = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance)
if not "watchers" in attrs:
return instance
new_watcher_ids = set(attrs.get("watchers", None)) #A partial update can exclude the watchers field or if the new instance can still not be saved
old_watcher_ids = set(instance.get_watchers().values_list("id", flat=True)) if instance is None or len(new_watcher_ids) == 0:
return obj
old_watcher_ids = set(obj.get_watchers().values_list("id", flat=True))
adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids))
removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids))
@ -204,14 +205,14 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer):
adding_users = User.objects.filter(id__in=adding_watcher_ids) adding_users = User.objects.filter(id__in=adding_watcher_ids)
removing_users = User.objects.filter(id__in=removing_watcher_ids) removing_users = User.objects.filter(id__in=removing_watcher_ids)
for user in adding_users: for user in adding_users:
services.add_watcher(instance, user) services.add_watcher(obj, user)
for user in removing_users: for user in removing_users:
services.remove_watcher(instance, user) services.remove_watcher(obj, user)
instance.watchers = instance.get_watchers() obj.watchers = obj.get_watchers()
return instance return obj
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

View File

@ -367,6 +367,23 @@ def get_watched(user_or_id, model):
params=(obj_type.id, user_id)) params=(obj_type.id, user_id))
def get_projects_watched(user_or_id):
"""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.
"""
if isinstance(user_or_id, get_user_model()):
user_id = user_or_id.id
else:
user_id = user_or_id
project_class = apps.get_model("projects", "Project")
return project_class.objects.filter(notify_policies__user__id=user_id).exclude(notify_policies__notify_level=NotifyLevel.ignore)
def add_watcher(obj, user): def add_watcher(obj, user):
"""Add a watcher to an object. """Add a watcher to an object.

View File

@ -165,27 +165,31 @@ class FavouriteSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
ref = serializers.IntegerField() ref = serializers.IntegerField()
slug = serializers.CharField() slug = serializers.CharField()
name = serializers.CharField()
subject = serializers.CharField() subject = serializers.CharField()
tags = TagsField(default=[]) description = serializers.SerializerMethodField("get_description")
project = serializers.IntegerField()
assigned_to = serializers.IntegerField() assigned_to = serializers.IntegerField()
total_watchers = serializers.IntegerField() status = serializers.CharField()
status_color = serializers.CharField()
tags_colors = serializers.SerializerMethodField("get_tags_color")
created_date = serializers.DateTimeField()
is_private = serializers.SerializerMethodField("get_is_private")
is_voted = serializers.SerializerMethodField("get_is_voted") is_voted = serializers.SerializerMethodField("get_is_voted")
is_watched = serializers.SerializerMethodField("get_is_watched") is_watched = serializers.SerializerMethodField("get_is_watched")
created_date = serializers.DateTimeField() total_watchers = serializers.IntegerField()
total_votes = serializers.IntegerField()
project_name = serializers.CharField() project = serializers.SerializerMethodField("get_project")
project_slug = serializers.CharField() project_name = serializers.SerializerMethodField("get_project_name")
project_is_private = serializers.CharField() project_slug = serializers.SerializerMethodField("get_project_slug")
project_is_private = serializers.SerializerMethodField("get_project_is_private")
assigned_to_username = serializers.CharField() assigned_to_username = serializers.CharField()
assigned_to_full_name = serializers.CharField() assigned_to_full_name = serializers.CharField()
assigned_to_photo = serializers.SerializerMethodField("get_photo") assigned_to_photo = serializers.SerializerMethodField("get_photo")
total_votes = serializers.IntegerField()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Don't pass the extra ids args up to the superclass # Don't pass the extra ids args up to the superclass
self.user_votes = kwargs.pop("user_votes", {}) self.user_votes = kwargs.pop("user_votes", {})
@ -194,6 +198,38 @@ class FavouriteSerializer(serializers.Serializer):
# Instantiate the superclass normally # Instantiate the superclass normally
super(FavouriteSerializer, self).__init__(*args, **kwargs) super(FavouriteSerializer, self).__init__(*args, **kwargs)
def _none_if_project(self, obj, property):
type = obj.get("type", "")
if type == "project":
return None
return obj.get(property)
def _none_if_not_project(self, obj, property):
type = obj.get("type", "")
if type != "project":
return None
return obj.get(property)
def get_project(self, obj):
return self._none_if_project(obj, "project")
def get_is_private(self, obj):
return self._none_if_not_project(obj, "project_is_private")
def get_project_name(self, obj):
return self._none_if_project(obj, "project_name")
def get_description(self, obj):
return self._none_if_not_project(obj, "description")
def get_project_slug(self, obj):
return self._none_if_project(obj, "project_slug")
def get_project_is_private(self, obj):
return self._none_if_project(obj, "project_is_private")
def get_is_voted(self, obj): def get_is_voted(self, obj):
return obj["id"] in self.user_votes.get(obj["type"], []) return obj["id"] in self.user_votes.get(obj["type"], [])
@ -201,6 +237,14 @@ class FavouriteSerializer(serializers.Serializer):
return obj["id"] in self.user_watching.get(obj["type"], []) return obj["id"] in self.user_watching.get(obj["type"], [])
def get_photo(self, obj): def get_photo(self, obj):
type = obj.get("type", "")
if type == "project":
return None
UserData = namedtuple("UserData", ["photo", "email"]) UserData = namedtuple("UserData", ["photo", "email"])
user_data = UserData(photo=obj["assigned_to_photo"], email=obj.get("assigned_to_email") or "") user_data = UserData(photo=obj["assigned_to_photo"], email=obj.get("assigned_to_email") or "")
return get_photo_or_gravatar_url(user_data) return get_photo_or_gravatar_url(user_data)
def get_tags_color(self, obj):
tags = obj.get("tags", [])
return [{"name": tc[0], "color": tc[1]} for tc in obj.get("tags_colors", []) if tc[0] in tags]

View File

@ -31,7 +31,7 @@ from easy_thumbnails.exceptions import InvalidImageFormatError
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base.utils.urls import get_absolute_url from taiga.base.utils.urls import get_absolute_url
from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.choices import NotifyLevel
from taiga.projects.notifications.services import get_projects_watched
from .gravatar import get_gravatar_url from .gravatar import get_gravatar_url
from django.conf import settings from django.conf import settings
@ -179,6 +179,11 @@ def get_watched_content_for_user(user):
list.append(object_id) list.append(object_id)
user_watches[ct_model] = list user_watches[ct_model] = list
#Now for projects,
projects_watched = get_projects_watched(user)
project_content_type_model=ContentType.objects.get(app_label="projects", model="project").model
user_watches[project_content_type_model] = projects_watched.values_list("id", flat=True)
return user_watches return user_watches
@ -186,8 +191,9 @@ def _build_favourites_sql_for_projects(for_user):
sql = """ sql = """
SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'watch' AS action, SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'watch' AS action,
tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project, tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project,
slug AS slug, projects_project.name AS subject, slug AS slug, projects_project.name AS name, null AS subject,
notifications_notifypolicy.created_at, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to notifications_notifypolicy.created_at as created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to,
null as status, null as status_color
FROM notifications_notifypolicy FROM notifications_notifypolicy
INNER JOIN projects_project INNER JOIN projects_project
ON (projects_project.id = notifications_notifypolicy.project_id) ON (projects_project.id = notifications_notifypolicy.project_id)
@ -203,8 +209,9 @@ def _build_favourites_sql_for_projects(for_user):
UNION UNION
SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'vote' AS action, SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'vote' AS action,
tags, votes_vote.object_id AS object_id, projects_project.id AS project, tags, votes_vote.object_id AS object_id, projects_project.id AS project,
slug AS slug, projects_project.name AS subject, slug AS slug, projects_project.name AS name, null AS subject,
votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to,
null as status, null as status_color
FROM votes_vote FROM votes_vote
INNER JOIN projects_project INNER JOIN projects_project
ON (projects_project.id = votes_vote.object_id) ON (projects_project.id = votes_vote.object_id)
@ -216,7 +223,7 @@ def _build_favourites_sql_for_projects(for_user):
ON projects_project.id = type_watchers.project_id ON projects_project.id = type_watchers.project_id
LEFT JOIN votes_votes LEFT JOIN votes_votes
ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id)
WHERE votes_vote.user_id = {for_user_id} WHERE votes_vote.user_id = {for_user_id} AND {project_content_type_id} = votes_vote.content_type_id
""" """
sql = sql.format( sql = sql.format(
for_user_id=for_user.id, for_user_id=for_user.id,
@ -232,13 +239,16 @@ def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref",
sql = """ sql = """
SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'watch' AS action, SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'watch' AS action,
tags, notifications_watched.object_id AS object_id, {table_name}.{project_column} AS project, tags, notifications_watched.object_id AS object_id, {table_name}.{project_column} AS project,
{slug_column} AS slug, {subject_column} AS subject, {slug_column} AS slug, null AS name, {subject_column} AS subject,
notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to,
projects_{type}status.name as status, projects_{type}status.color as status_color
FROM notifications_watched FROM notifications_watched
INNER JOIN django_content_type INNER JOIN django_content_type
ON (notifications_watched.content_type_id = django_content_type.id AND django_content_type.model = '{type}') ON (notifications_watched.content_type_id = django_content_type.id AND django_content_type.model = '{type}')
INNER JOIN {table_name} INNER JOIN {table_name}
ON ({table_name}.id = notifications_watched.object_id) ON ({table_name}.id = notifications_watched.object_id)
INNER JOIN projects_{type}status
ON (projects_{type}status.id = {table_name}.status_id)
LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers
ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id
LEFT JOIN votes_votes LEFT JOIN votes_votes
@ -247,13 +257,16 @@ def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref",
UNION UNION
SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'vote' AS action, SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'vote' AS action,
tags, votes_vote.object_id AS object_id, {table_name}.{project_column} AS project, tags, votes_vote.object_id AS object_id, {table_name}.{project_column} AS project,
{slug_column} AS slug, {subject_column} AS subject, {slug_column} AS slug, null AS name, {subject_column} AS subject,
votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to,
projects_{type}status.name as status, projects_{type}status.color as status_color
FROM votes_vote FROM votes_vote
INNER JOIN django_content_type INNER JOIN django_content_type
ON (votes_vote.content_type_id = django_content_type.id AND django_content_type.model = '{type}') ON (votes_vote.content_type_id = django_content_type.id AND django_content_type.model = '{type}')
INNER JOIN {table_name} INNER JOIN {table_name}
ON ({table_name}.id = votes_vote.object_id) ON ({table_name}.id = votes_vote.object_id)
INNER JOIN projects_{type}status
ON (projects_{type}status.id = {table_name}.status_id)
LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers
ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id
LEFT JOIN votes_votes LEFT JOIN votes_votes
@ -279,12 +292,18 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None):
filters_sql += " AND action = '{action}' ".format(action=action) filters_sql += " AND action = '{action}' ".format(action=action)
if q: if q:
filters_sql += " AND to_tsvector(coalesce(subject, '')) @@ plainto_tsquery('{q}') ".format(q=q) # We must transform a q like "proj exam" (should find "project example") to something like proj:* & exam:*
qs = ["{}:*".format(e) for e in q.split(" ")]
filters_sql += """ AND (
to_tsvector(coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('{q}')
)
""".format(q=" & ".join(qs))
sql = """ sql = """
-- BEGIN Basic info: we need to mix info from different tables and denormalize it -- BEGIN Basic info: we need to mix info from different tables and denormalize it
SELECT entities.*, SELECT entities.*,
projects_project.name as project_name, projects_project.slug as project_slug, projects_project.is_private as project_is_private, projects_project.name as project_name, projects_project.description as description, projects_project.slug as project_slug, projects_project.is_private as project_is_private,
projects_project.tags_colors,
users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email
FROM ( FROM (
{userstories_sql} {userstories_sql}
@ -332,7 +351,7 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None):
-- END Permissions checking -- END Permissions checking
{filters_sql} {filters_sql}
ORDER BY entities.created_date; ORDER BY entities.created_date DESC;
""" """
from_user_id = -1 from_user_id = -1

View File

@ -9,6 +9,7 @@ from .. import factories as f
from taiga.base.utils import json from taiga.base.utils import json
from taiga.users import models from taiga.users import models
from taiga.users.serializers import FavouriteSerializer
from taiga.auth.tokens import get_token_for_user from taiga.auth.tokens import get_token_for_user
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
from taiga.users.services import get_favourites_list from taiga.users.services import get_favourites_list
@ -396,32 +397,43 @@ def test_get_favourites_list_valid_info_for_project():
viewer_user = f.UserFactory() viewer_user = f.UserFactory()
watcher_user = f.UserFactory() watcher_user = f.UserFactory()
project = f.ProjectFactory(is_private=False, name="Testing project") project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag'])
project.add_watcher(watcher_user) project.add_watcher(watcher_user)
content_type = ContentType.objects.get_for_model(project) content_type = ContentType.objects.get_for_model(project)
vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user)
f.VotesFactory(content_type=content_type, object_id=project.id, count=1) f.VotesFactory(content_type=content_type, object_id=project.id, count=1)
project_vote_info = get_favourites_list(fav_user, viewer_user)[0] raw_project_vote_info = get_favourites_list(fav_user, viewer_user)[0]
project_vote_info = FavouriteSerializer(raw_project_vote_info).data
assert project_vote_info["type"] == "project" assert project_vote_info["type"] == "project"
assert project_vote_info["action"] == "vote" assert project_vote_info["action"] == "vote"
assert project_vote_info["id"] == project.id assert project_vote_info["id"] == project.id
assert project_vote_info["ref"] == None assert project_vote_info["ref"] == None
assert project_vote_info["slug"] == project.slug assert project_vote_info["slug"] == project.slug
assert project_vote_info["subject"] == project.name assert project_vote_info["name"] == project.name
assert project_vote_info["tags"] == project.tags assert project_vote_info["subject"] == None
assert project_vote_info["project"] == project.id assert project_vote_info["description"] == project.description
assert project_vote_info["assigned_to"] == None assert project_vote_info["assigned_to"] == None
assert project_vote_info["status"] == None
assert project_vote_info["status_color"] == None
tags_colors = {tc["name"]:tc["color"] for tc in project_vote_info["tags_colors"]}
assert "test" in tags_colors
assert "tag" in tags_colors
assert project_vote_info["is_private"] == project.is_private
assert project_vote_info["is_voted"] == False
assert project_vote_info["is_watched"] == False
assert project_vote_info["total_watchers"] == 1 assert project_vote_info["total_watchers"] == 1
assert project_vote_info["created_date"] == vote.created_date assert project_vote_info["total_votes"] == 1
assert project_vote_info["project_name"] == project.name assert project_vote_info["project"] == None
assert project_vote_info["project_slug"] == project.slug assert project_vote_info["project_name"] == None
assert project_vote_info["project_is_private"] == project.is_private assert project_vote_info["project_slug"] == None
assert project_vote_info["project_is_private"] == None
assert project_vote_info["assigned_to_username"] == None assert project_vote_info["assigned_to_username"] == None
assert project_vote_info["assigned_to_full_name"] == None assert project_vote_info["assigned_to_full_name"] == None
assert project_vote_info["assigned_to_photo"] == None assert project_vote_info["assigned_to_photo"] == None
assert project_vote_info["assigned_to_email"] == None
assert project_vote_info["total_votes"] == 1
def test_get_favourites_list_valid_info_for_not_project_types(): def test_get_favourites_list_valid_info_for_not_project_types():
@ -449,26 +461,37 @@ def test_get_favourites_list_valid_info_for_not_project_types():
vote = f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) vote = f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user)
f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) f.VotesFactory(content_type=content_type, object_id=instance.id, count=3)
instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0] raw_instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0]
instance_vote_info = FavouriteSerializer(raw_instance_vote_info).data
assert instance_vote_info["type"] == object_type assert instance_vote_info["type"] == object_type
assert instance_vote_info["action"] == "vote" assert instance_vote_info["action"] == "vote"
assert instance_vote_info["id"] == instance.id assert instance_vote_info["id"] == instance.id
assert instance_vote_info["ref"] == instance.ref assert instance_vote_info["ref"] == instance.ref
assert instance_vote_info["slug"] == None assert instance_vote_info["slug"] == None
assert instance_vote_info["name"] == None
assert instance_vote_info["subject"] == instance.subject assert instance_vote_info["subject"] == instance.subject
assert instance_vote_info["tags"] == instance.tags assert instance_vote_info["description"] == None
assert instance_vote_info["project"] == instance.project.id assert instance_vote_info["assigned_to"] == instance.assigned_to.id
assert instance_vote_info["assigned_to"] == assigned_to_user.id assert instance_vote_info["status"] == instance.status.name
assert instance_vote_info["status_color"] == instance.status.color
tags_colors = {tc["name"]:tc["color"] for tc in instance_vote_info["tags_colors"]}
assert "test1" in tags_colors
assert "test2" in tags_colors
assert instance_vote_info["is_private"] == None
assert instance_vote_info["is_voted"] == False
assert instance_vote_info["is_watched"] == False
assert instance_vote_info["total_watchers"] == 1 assert instance_vote_info["total_watchers"] == 1
assert instance_vote_info["created_date"] == vote.created_date assert instance_vote_info["total_votes"] == 3
assert instance_vote_info["project"] == instance.project.id
assert instance_vote_info["project_name"] == instance.project.name assert instance_vote_info["project_name"] == instance.project.name
assert instance_vote_info["project_slug"] == instance.project.slug assert instance_vote_info["project_slug"] == instance.project.slug
assert instance_vote_info["project_is_private"] == instance.project.is_private assert instance_vote_info["project_is_private"] == instance.project.is_private
assert instance_vote_info["assigned_to_username"] == assigned_to_user.username assert instance_vote_info["assigned_to_username"] == instance.assigned_to.username
assert instance_vote_info["assigned_to_full_name"] == assigned_to_user.full_name assert instance_vote_info["assigned_to_full_name"] == instance.assigned_to.full_name
assert instance_vote_info["assigned_to_photo"] == '' assert instance_vote_info["assigned_to_photo"] != ""
assert instance_vote_info["assigned_to_email"] == assigned_to_user.email
assert instance_vote_info["total_votes"] == 3
def test_get_favourites_list_permissions(): def test_get_favourites_list_permissions():

View File

@ -46,6 +46,22 @@ def test_update_userstories_order_in_bulk():
model=models.UserStory) model=models.UserStory)
def test_create_userstory_with_watchers(client):
user = f.UserFactory.create()
user_watcher = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
f.MembershipFactory.create(project=project, user=user, is_owner=True)
f.MembershipFactory.create(project=project, user=user_watcher, is_owner=True)
url = reverse("userstories-list")
data = {"subject": "Test user story", "project": project.id, "watchers": [user_watcher.id]}
client.login(user)
response = client.json.post(url, json.dumps(data))
print(response.data)
assert response.status_code == 201
assert response.data["watchers"] == []
def test_create_userstory_without_status(client): def test_create_userstory_without_status(client):
user = f.UserFactory.create() user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user) project = f.ProjectFactory.create(owner=user)