Browse Source

Merge pull request #1733 from doccano/fix/projectStaff

Enable to view dataset page for project staffs
pull/1738/head v1.6.2
Hiroki Nakayama 2 years ago
committed by GitHub
parent
commit
9e4692c888
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 47 additions and 13 deletions
  1. 18
      backend/projects/tests/test_member.py
  2. 3
      backend/projects/urls.py
  3. 13
      backend/projects/views/member.py
  4. 2
      frontend/domain/models/member/memberRepository.ts
  5. 3
      frontend/layouts/project.vue
  6. 3
      frontend/layouts/workspace.vue
  7. 2
      frontend/middleware/check-admin.js
  8. 4
      frontend/pages/projects/_id/dataset/index.vue
  9. 6
      frontend/repositories/member/apiMemberRepository.ts
  10. 6
      frontend/services/application/member/memberApplicationService.ts

18
backend/projects/tests/test_member.py

@ -116,6 +116,24 @@ class TestMemberFilter(CRUDMixin):
self.assertEqual(len(response.data), 1)
class TestMyRole(CRUDMixin):
def setUp(self):
self.project = prepare_project()
self.url = reverse(viewname="my_role", args=[self.project.item.id])
def test_admin(self):
response = self.assert_fetch(self.project.admin, status.HTTP_200_OK)
self.assertEqual(response.data["rolename"], settings.ROLE_PROJECT_ADMIN)
def test_approver(self):
response = self.assert_fetch(self.project.approver, status.HTTP_200_OK)
self.assertEqual(response.data["rolename"], settings.ROLE_ANNOTATION_APPROVER)
def test_annotator(self):
response = self.assert_fetch(self.project.annotator, status.HTTP_200_OK)
self.assertEqual(response.data["rolename"], settings.ROLE_ANNOTATOR)
class TestMemberManager(CRUDMixin):
def test_has_role(self):
project = prepare_project()

3
backend/projects/urls.py

@ -1,12 +1,13 @@
from django.urls import path
from .views.member import MemberDetail, MemberList
from .views.member import MemberDetail, MemberList, MyRole
from .views.project import ProjectDetail, ProjectList
from .views.tag import TagDetail, TagList
urlpatterns = [
path(route="projects", view=ProjectList.as_view(), name="project_list"),
path(route="projects/<int:project_id>", view=ProjectDetail.as_view(), name="project_detail"),
path(route="projects/<int:project_id>/my-role", view=MyRole.as_view(), name="my_role"),
path(route="projects/<int:project_id>/tags", view=TagList.as_view(), name="tag_list"),
path(route="projects/<int:project_id>/tags/<int:tag_id>", view=TagDetail.as_view(), name="tag_detail"),
path(route="projects/<int:project_id>/members", view=MemberList.as_view(), name="member_list"),

13
backend/projects/views/member.py

@ -1,4 +1,5 @@
from django.db import IntegrityError
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated
@ -6,7 +7,7 @@ from rest_framework.response import Response
from projects.exceptions import RoleAlreadyAssignedException, RoleConstraintException
from projects.models import Member
from projects.permissions import IsProjectAdmin
from projects.permissions import IsProjectAdmin, IsProjectMember
from projects.serializers import MemberSerializer
@ -51,3 +52,13 @@ class MemberDetail(generics.RetrieveUpdateAPIView):
super().perform_update(serializer)
except IntegrityError:
raise RoleAlreadyAssignedException
class MyRole(generics.RetrieveAPIView):
queryset = Member.objects.all()
serializer_class = MemberSerializer
permission_classes = [IsAuthenticated & IsProjectMember]
def get_object(self):
kwargs = {"user": self.request.user, "project_id": self.kwargs["project_id"]}
return get_object_or_404(self.queryset, **kwargs)

2
frontend/domain/models/member/memberRepository.ts

@ -8,4 +8,6 @@ export interface MemberRepository {
update(projectId: string, item: MemberItem): Promise<MemberItem>
bulkDelete(projectId: string, memberIds: number[]): Promise<void>
fetchMyRole(projectId: string): Promise<MemberItem>
}

3
frontend/layouts/project.vue

@ -58,11 +58,10 @@ export default {
computed: {
...mapGetters('projects', ['getLink', 'currentProject']),
...mapGetters('auth', ['getUserId'])
},
async created() {
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.$route.params.id, this.getUserId)
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.$route.params.id)
}
}
</script>

3
frontend/layouts/workspace.vue

@ -46,7 +46,6 @@ export default {
computed: {
...mapGetters('projects', ['getLink', 'currentProject']),
...mapGetters('auth', ['getUserId'])
},
watch: {
@ -56,7 +55,7 @@ export default {
},
async created() {
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.$route.params.id, this.getUserId)
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.$route.params.id)
}
}
</script>

2
frontend/middleware/check-admin.js

@ -7,7 +7,7 @@ export default _.debounce(async function({ app, store, route, redirect }) {
redirect('/projects')
}
const userId = store.getters['auth/getUserId']
const isProjectAdmin = await app.$services.member.isProjectAdmin(route.params.id, userId)
const isProjectAdmin = await app.$services.member.isProjectAdmin(route.params.id)
const projectRoot = app.localePath('/projects/' + route.params.id)
const path = route.fullPath.replace(/\/$/g, '')

4
frontend/pages/projects/_id/dataset/index.vue

@ -69,7 +69,6 @@
<script lang="ts">
import Vue from 'vue'
import { mapGetters } from 'vuex'
import _ from 'lodash'
import DocumentList from '@/components/example/DocumentList.vue'
import FormDelete from '@/components/example/FormDelete.vue'
@ -136,7 +135,6 @@ export default Vue.extend({
return 'text'
}
},
...mapGetters('auth', ['getUserId'])
},
watch: {
@ -149,7 +147,7 @@ export default Vue.extend({
async created() {
this.project = await this.$services.project.findById(this.projectId)
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.projectId, this.getUserId)
this.isProjectAdmin = await this.$services.member.isProjectAdmin(this.projectId)
},
methods: {

6
frontend/repositories/member/apiMemberRepository.ts

@ -30,4 +30,10 @@ export class APIMemberRepository implements MemberRepository {
const url = `/projects/${projectId}/members`
await this.request.delete(url, { ids: memberIds })
}
async fetchMyRole(projectId: string): Promise<MemberItem> {
const url = `/projects/${projectId}/my-role`
const response = await this.request.get(url)
return plainToInstance(MemberItem, response.data)
}
}

6
frontend/services/application/member/memberApplicationService.ts

@ -40,8 +40,8 @@ export class MemberApplicationService {
return this.repository.bulkDelete(projectId, ids)
}
public async isProjectAdmin(projectId: string, userId: number): Promise<boolean> {
const items = await this.repository.list(projectId)
return items.some((item) => item.user === userId && item.isProjectAdmin)
public async isProjectAdmin(projectId: string): Promise<boolean> {
const item = await this.repository.fetchMyRole(projectId)
return item.isProjectAdmin
}
}
Loading…
Cancel
Save