Browse Source

Merge pull request #216 from CatalystCode/feature/random-document-sort-order

Feature/Add option to randomize document order per user
pull/239/head
Hiroki Nakayama 5 years ago
committed by GitHub
parent
commit
0c1414348f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 7 deletions
  1. 10
      app/server/api.py
  2. 2
      app/server/forms.py
  3. 18
      app/server/migrations/0002_project_randomize_document_order.py
  4. 1
      app/server/models.py
  5. 12
      app/server/serializers.py
  6. 13
      app/server/static/components/projects.vue
  7. 32
      app/server/tests/test_api.py

10
app/server/api.py

@ -3,7 +3,7 @@ from collections import Counter
from django.conf import settings
from django.shortcuts import get_object_or_404, redirect
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Count
from django.db.models import Count, F
from libcloud.base import DriverType, get_driver
from libcloud.storage.types import ContainerDoesNotExistError, ObjectDoesNotExistError
from rest_framework import generics, filters, status
@ -129,7 +129,13 @@ class DocumentList(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated, IsProjectUser, IsAdminUserAndWriteOnly)
def get_queryset(self):
queryset = self.queryset.filter(project=self.kwargs['project_id'])
project = get_object_or_404(Project, pk=self.kwargs['project_id'])
queryset = self.queryset.filter(project=project)
if project.randomize_document_order:
queryset = queryset.annotate(sort_id=F('id') % self.request.user.id).order_by('sort_id')
return queryset
def perform_create(self, serializer):

2
app/server/forms.py

@ -7,4 +7,4 @@ class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ('name', 'description', 'project_type', 'users')
fields = ('name', 'description', 'project_type', 'users', 'randomize_document_order')

18
app/server/migrations/0002_project_randomize_document_order.py

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-05-22 18:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('server', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='project',
name='randomize_document_order',
field=models.BooleanField(default=False),
),
]

1
app/server/models.py

@ -25,6 +25,7 @@ class Project(PolymorphicModel):
updated_at = models.DateTimeField(auto_now=True)
users = models.ManyToManyField(User, related_name='projects')
project_type = models.CharField(max_length=30, choices=PROJECT_CHOICES)
randomize_document_order = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('upload', args=[self.id])

12
app/server/serializers.py

@ -75,7 +75,8 @@ class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at')
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at',
'randomize_document_order')
read_only_fields = ('image', 'updated_at')
@ -83,7 +84,8 @@ class TextClassificationProjectSerializer(serializers.ModelSerializer):
class Meta:
model = TextClassificationProject
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at')
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at',
'randomize_document_order')
read_only_fields = ('image', 'updated_at', 'users')
@ -91,7 +93,8 @@ class SequenceLabelingProjectSerializer(serializers.ModelSerializer):
class Meta:
model = SequenceLabelingProject
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at')
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at',
'randomize_document_order')
read_only_fields = ('image', 'updated_at', 'users')
@ -99,7 +102,8 @@ class Seq2seqProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Seq2seqProject
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at')
fields = ('id', 'name', 'description', 'guideline', 'users', 'project_type', 'image', 'updated_at',
'randomize_document_order')
read_only_fields = ('image', 'updated_at', 'users')

13
app/server/static/components/projects.vue

@ -42,6 +42,17 @@
option(value="Seq2seq") sequence to sequence
p.help.is-danger {{ projectTypeError }}
div.field
label.checkbox
input(
v-model="randomizeDocumentOrder"
name="randomize_document_order"
type="checkbox"
style="margin-right: 0.25em;"
required
)
| Randomize document order per user
footer.modal-card-foot.pt20.pb20.pr20.pl20.has-background-white-ter
button.button.is-primary(v-on:click="create()") Create
button.button(v-on:click="isActive = !isActive") Cancel
@ -131,6 +142,7 @@ export default {
projectNameError: '',
username: '',
isSuperuser: false,
randomizeDocumentOrder: false,
}),
computed: {
@ -182,6 +194,7 @@ export default {
name: this.projectName,
description: this.description,
project_type: this.projectType,
randomize_document_order: this.randomizeDocumentOrder,
guideline: 'Please write annotation guideline.',
resourcetype: this.resourceType(),
};

32
app/server/tests/test_api.py

@ -297,9 +297,14 @@ class TestDocumentListAPI(APITestCase):
cls.main_project = mommy.make('server.TextClassificationProject', users=[project_member, super_user])
mommy.make('server.Document', project=cls.main_project)
cls.random_order_project = mommy.make('server.TextClassificationProject', users=[project_member, super_user],
randomize_document_order=True)
mommy.make('server.Document', 100, project=cls.random_order_project)
sub_project = mommy.make('server.TextClassificationProject', users=[non_project_member])
mommy.make('server.Document', project=sub_project)
cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
cls.random_order_project_url = reverse(viewname='doc_list', args=[cls.random_order_project.id])
cls.data = {'text': 'example'}
def test_returns_docs_to_project_member(self):
@ -308,6 +313,33 @@ class TestDocumentListAPI(APITestCase):
response = self.client.get(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_returns_docs_in_consistent_order_for_all_users(self):
self.client.login(username=self.project_member_name, password=self.project_member_pass)
user1_documents = self.client.get(self.url, format='json').json().get('results')
self.client.logout()
self.client.login(username=self.super_user_name, password=self.super_user_pass)
user2_documents = self.client.get(self.url, format='json').json().get('results')
self.client.logout()
self.assertEqual(user1_documents, user2_documents)
def test_can_return_docs_in_consistent_random_order(self):
self.client.login(username=self.project_member_name, password=self.project_member_pass)
user1_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
user1_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
self.client.logout()
self.assertEqual(user1_documents1, user1_documents2)
self.client.login(username=self.super_user_name, password=self.super_user_pass)
user2_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
user2_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
self.client.logout()
self.assertEqual(user2_documents1, user2_documents2)
self.assertNotEqual(user1_documents1, user2_documents1)
self.assertNotEqual(user1_documents2, user2_documents2)
def test_do_not_return_docs_to_non_project_member(self):
self.client.login(username=self.non_project_member_name,
password=self.non_project_member_pass)

Loading…
Cancel
Save