diff --git a/taiga/projects/api.py b/taiga/projects/api.py index e18d3807..c3753de5 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -324,3 +324,21 @@ class ProjectTemplateViewSet(ModelCrudViewSet): def get_queryset(self): return models.ProjectTemplate.objects.all() + + +class FansViewSet(ModelCrudViewSet): + serializer_class = serializers.FanSerializer + list_serializer_class = serializers.FanSerializer + permission_classes = (IsAuthenticated,) + + def get_queryset(self): + return stars.get_fans(self.kwargs.get("project_id")) + + +class StarredViewSet(ModelCrudViewSet): + serializer_class = serializers.StarredSerializer + list_serializer_class = serializers.StarredSerializer + permission_classes = (IsAuthenticated,) + + def get_queryset(self): + return stars.get_starred(self.kwargs.get("user_id")) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 8ca57b96..b5760f15 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -19,7 +19,7 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from taiga.base.serializers import PickleField, JsonField -from taiga.users.models import Role +from taiga.users.models import Role, User from . import models @@ -84,6 +84,7 @@ class ProjectMembershipSerializer(serializers.ModelSerializer): class ProjectSerializer(serializers.ModelSerializer): tags = PickleField(required=False) + stars = serializers.IntegerField(source="stars.count") class Meta: model = models.Project @@ -150,3 +151,17 @@ class ProjectTemplateSerializer(serializers.ModelSerializer): class Meta: model = models.ProjectTemplate + + +class FanSerializer(serializers.ModelSerializer): + full_name = serializers.CharField(source='get_full_name', required=False) + + class Meta: + model = User + fields = ('id', 'username', 'first_name', 'last_name', 'full_name') + + +class StarredSerializer(serializers.ModelSerializer): + class Meta: + model = models.Project + fields = ['id', 'name', 'slug'] diff --git a/taiga/projects/stars/__init__.py b/taiga/projects/stars/__init__.py index 87225831..a95e2bd7 100644 --- a/taiga/projects/stars/__init__.py +++ b/taiga/projects/stars/__init__.py @@ -1 +1 @@ -from .services import star, unstar +from .services import star, unstar, get_fans, get_starred diff --git a/taiga/projects/stars/models.py b/taiga/projects/stars/models.py index 0abe06b4..3ee44863 100644 --- a/taiga/projects/stars/models.py +++ b/taiga/projects/stars/models.py @@ -24,7 +24,7 @@ class Fan(models.Model): class Stars(models.Model): project = models.OneToOneField("projects.Project", null=False, blank=False, - verbose_name=_("project")) + related_name="stars", verbose_name=_("project")) count = models.PositiveIntegerField(null=False, blank=False, default=0, verbose_name=_("count")) diff --git a/taiga/projects/stars/services.py b/taiga/projects/stars/services.py index 8f6e48b5..0ed4ef83 100644 --- a/taiga/projects/stars/services.py +++ b/taiga/projects/stars/services.py @@ -45,11 +45,23 @@ def get_stars(project): return Stars.objects.filter(project=project).count -def get_fans(project): +def get_fans(project_or_id): """Get the fans a project have.""" - return get_user_model().objects.filter(fans__project=project) + qs = get_user_model().objects.get_queryset() + if isinstance(project_or_id, int): + qs = qs.filter(fans__project_id=project_or_id) + else: + qs = qs.filter(fans__project=project_or_id) + + return qs -def get_starred_projects(user): +def get_starred(user_or_id): """Get the projects an user has starred.""" - return Project.objects.filter(fans__user=user) + qs = Project.objects.get_queryset() + if isinstance(user_or_id, int): + qs = qs.filter(fans__user_id=user_or_id) + else: + qs = qs.filter(fans__user=user_or_id) + + return qs diff --git a/taiga/routers.py b/taiga/routers.py index 436b07d6..5c8d882d 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -59,9 +59,13 @@ from taiga.projects.api import IssueTypeViewSet from taiga.projects.api import PriorityViewSet from taiga.projects.api import SeverityViewSet from taiga.projects.api import ProjectTemplateViewSet +from taiga.projects.api import FansViewSet +from taiga.projects.api import StarredViewSet router.register(r"roles", RolesViewSet, base_name="roles") router.register(r"projects", ProjectViewSet, base_name="projects") +router.register(r"projects/(?P\d+)/fans", FansViewSet, base_name="project-fans") +router.register(r"users/(?P\d+)/starred", StarredViewSet, base_name="user-starred") router.register(r"project-templates", ProjectTemplateViewSet, base_name="project-templates") router.register(r"memberships", MembershipViewSet, base_name="memberships") router.register(r"invitations", InvitationViewSet, base_name="invitations") diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 09817ba2..dd006973 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -18,6 +18,8 @@ from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import Permission from rest_framework import serializers + +from taiga.projects.models import Project from .models import User, Role diff --git a/tests/factories.py b/tests/factories.py index fdf83324..a852d60a 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -164,3 +164,10 @@ class FanFactory(Factory): project = factory.SubFactory("tests.factories.ProjectFactory") user = factory.SubFactory("tests.factories.UserFactory") + + +class StarsFactory(Factory): + FACTORY_FOR = taiga.projects.stars.models.Stars + + project = factory.SubFactory("tests.factories.ProjectFactory") + count = 0 diff --git a/tests/integration/test_stars.py b/tests/integration/test_stars.py index 754115a0..eea0333c 100644 --- a/tests/integration/test_stars.py +++ b/tests/integration/test_stars.py @@ -52,3 +52,66 @@ def test_project_member_unstar_project(client): response = client.post(url) assert response.status_code == 200 + + +def test_list_project_fans(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + fan = f.FanFactory.create(project=project) + url = reverse("project-fans-list", args=(project.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == fan.user.id + + +def test_get_project_fan(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + fan = f.FanFactory.create(project=project) + url = reverse("project-fans-detail", args=(project.id, fan.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == fan.user.id + + +def test_list_user_starred_projects(client): + user = f.UserFactory.create() + fan = f.FanFactory.create(user=user) + url = reverse("user-starred-list", args=(user.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == fan.project.id + + +def test_get_user_starred_project(client): + user = f.UserFactory.create() + fan = f.FanFactory.create(user=user) + url = reverse("user-starred-detail", args=(user.id, fan.project.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == fan.project.id + + +def test_get_project_stars(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + f.StarsFactory.create(project=project, count=5) + url = reverse("projects-detail", args=(project.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['stars'] == 5