Skip to content

Commit

Permalink
Merge pull request #221 from Ilhasoft/develop
Browse files Browse the repository at this point in the history
Version 1.16.0
  • Loading branch information
Douglas Paz committed Oct 10, 2018
2 parents d09908c + 46a855f commit e6afaf6
Show file tree
Hide file tree
Showing 15 changed files with 649 additions and 42 deletions.
2 changes: 2 additions & 0 deletions bothub/api/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from .views import RepositoryAuthorizationRequestsViewSet
from .views import ReviewAuthorizationRequestViewSet
from .views import RepositoryEntitiesViewSet
from .views import RepositoryUpdatesViewSet


class Router(routers.SimpleRouter):
Expand Down Expand Up @@ -121,3 +122,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''):
router.register('review-authorization-request',
ReviewAuthorizationRequestViewSet)
router.register('entities', RepositoryEntitiesViewSet)
router.register('updates', RepositoryUpdatesViewSet)
4 changes: 4 additions & 0 deletions bothub/api/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@
RequestRepositoryAuthorizationSerializer,
ReviewAuthorizationRequestSerializer,
)

from .update import ( # noqa: F401
RepositoryUpdateSerializer,
)
7 changes: 7 additions & 0 deletions bothub/api/serializers/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class Meta:
'name',
'slug',
'language',
'use_language_model_featurizer',
'use_competing_intents',
'categories',
'description',
'is_private',
Expand Down Expand Up @@ -65,6 +67,8 @@ class Meta:
'slug',
'language',
'available_languages',
'use_language_model_featurizer',
'use_competing_intents',
'categories',
'categories_list',
'description',
Expand All @@ -78,6 +82,9 @@ class Meta:
'available_request_authorization',
'request_authorization',
'ready_for_train',
'requirements_to_train',
'languages_ready_for_train',
'languages_warnings',
'votes_sum',
'created_at',
]
Expand Down
24 changes: 24 additions & 0 deletions bothub/api/serializers/update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from rest_framework import serializers

from bothub.common.models import RepositoryUpdate


class RepositoryUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = RepositoryUpdate
fields = [
'id',
'repository',
'language',
'created_at',
'by',
'by__nickname',
'training_started_at',
'trained_at',
'failed_at',
]

by__nickname = serializers.SlugRelatedField(
source='by',
slug_field='nickname',
read_only=True)
78 changes: 78 additions & 0 deletions bothub/api/tests/test_updates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import json

from django.test import TestCase
from django.test import RequestFactory
from rest_framework import status
from bothub.common.models import Repository
from bothub.common.models import RepositoryExample
from bothub.common import languages

from ..views import RepositoryUpdatesViewSet
from .utils import create_user_and_token


class UpdatesTestCase(TestCase):
def setUp(self):
self.factory = RequestFactory()

self.owner, self.owner_token = create_user_and_token('owner')

self.repository = Repository.objects.create(
owner=self.owner,
name='Testing',
slug='test',
language=languages.LANGUAGE_EN)
current_update = self.repository.current_update()
RepositoryExample.objects.create(
repository_update=current_update,
text='my name is Douglas',
intent='greet')
RepositoryExample.objects.create(
repository_update=current_update,
text='my name is John',
intent='greet')
current_update.start_training(self.owner)

def request(self, data, token=None):
authorization_header = {
'HTTP_AUTHORIZATION': 'Token {}'.format(token.key),
} if token else {}
request = self.factory.get(
'/api/updates/',
data,
**authorization_header)
response = RepositoryUpdatesViewSet.as_view(
{'get': 'list'})(request)
response.render()
content_data = json.loads(response.content)
return (response, content_data,)

def test_okay(self):
response, content_data = self.request(
{
'repository_uuid': str(self.repository.uuid),
},
self.owner_token)
self.assertEqual(
response.status_code,
status.HTTP_200_OK)
self.assertEqual(
content_data.get('count'),
1)

def test_not_authenticated(self):
response, content_data = self.request(
{
'repository_uuid': str(self.repository.uuid),
})
self.assertEqual(
response.status_code,
status.HTTP_401_UNAUTHORIZED)

def test_without_repository(self):
response, content_data = self.request(
{},
self.owner_token)
self.assertEqual(
response.status_code,
status.HTTP_400_BAD_REQUEST)
67 changes: 56 additions & 11 deletions bothub/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from bothub.common.models import RepositoryAuthorization
from bothub.common.models import RequestRepositoryAuthorization
from bothub.common.models import RepositoryEntity
from bothub.common.models import RepositoryUpdate
from bothub.authentication.models import User

from .serializers import RepositorySerializer
Expand All @@ -52,6 +53,7 @@
from .serializers import RequestRepositoryAuthorizationSerializer
from .serializers import ReviewAuthorizationRequestSerializer
from .serializers import RepositoryEntitySerializer
from .serializers import RepositoryUpdateSerializer


# Permisions
Expand Down Expand Up @@ -91,16 +93,6 @@ def has_object_permission(self, request, view, obj):
return authorization.can_contribute


class RepositoryTranslatedExampleEntityPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
repository = obj.repository_translated_example.original_example \
.repository_update.repository
authorization = repository.get_user_authorization(request.user)
if request.method in READ_METHODS:
return authorization.can_read
return authorization.can_contribute


class RepositoryAdminManagerAuthorization(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
authorization = obj.repository.get_user_authorization(request.user)
Expand All @@ -119,6 +111,18 @@ def has_object_permission(self, request, view, obj):
return False


class RepositoryUpdateHasPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
authorization = obj.repository.get_user_authorization(request.user)
if request.method in READ_METHODS:
return authorization.can_read
if request.user.is_authenticated:
if request.method in WRITE_METHODS:
return authorization.can_write
return authorization.is_admin
return False


# Filters

class ExamplesFilter(filters.FilterSet):
Expand Down Expand Up @@ -327,6 +331,34 @@ def filter_repository_uuid(self, queryset, name, value):
raise NotFound(_('Invalid repository UUID'))


class RepositoryUpdatesFilter(filters.FilterSet):
class Meta:
model = RepositoryUpdate
fields = [
'repository_uuid',
]

repository_uuid = filters.CharFilter(
field_name='repository_uuid',
required=True,
method='filter_repository_uuid',
help_text=_('Repository\'s UUID'))

def filter_repository_uuid(self, queryset, name, value):
request = self.request
try:
repository = Repository.objects.get(uuid=value)
authorization = repository.get_user_authorization(request.user)
if not authorization.can_read:
raise PermissionDenied()
return queryset.filter(repository=repository)
except Repository.DoesNotExist:
raise NotFound(
_('Repository {} does not exist').format(value))
except DjangoValidationError:
raise NotFound(_('Invalid repository UUID'))


# Mixins

class MultipleFieldLookupMixin(object):
Expand Down Expand Up @@ -478,7 +510,7 @@ def analyze(self, request, **kwargs):
return Response(request.json()) # pragma: no cover

response = None # pragma: no cover
try:
try: # pragma: no cover
response = request.json() # pragma: no cover
except Exception:
pass
Expand Down Expand Up @@ -951,3 +983,16 @@ class RepositoryEntitiesViewSet(
IsAuthenticated,
RepositoryEntityHasPermission,
]


class RepositoryUpdatesViewSet(
mixins.ListModelMixin,
GenericViewSet):
queryset = RepositoryUpdate.objects.filter(
training_started_at__isnull=False)
serializer_class = RepositoryUpdateSerializer
filter_class = RepositoryUpdatesFilter
permission_classes = [
IsAuthenticated,
RepositoryUpdateHasPermission,
]
3 changes: 3 additions & 0 deletions bothub/common/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ class RepositoryUpdateInline(admin.TabularInline):

fields = [
'language',
'use_language_model_featurizer',
'use_competing_intents',
'created_at',
'by',
'training_started_at',
'trained_at',
'failed_at',
'training_log',
'download_bot_data',
]
readonly_fields = fields
Expand Down
26 changes: 26 additions & 0 deletions bothub/common/migrations/0021_auto_20180921_1259.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 2.0.6 on 2018-09-21 12:59

import django.core.validators
from django.db import migrations, models
import re


def populate_empty_intent(apps, *args):
RepositoryExample = apps.get_model('common', 'RepositoryExample')
RepositoryExample.objects.filter(intent='').update(intent='no_intent')


class Migration(migrations.Migration):

dependencies = [
('common', '0020_auto_20180813_1320'),
]

operations = [
migrations.AlterField(
model_name='repositoryexample',
name='intent',
field=models.CharField(default='no_intent', help_text='Example intent reference', max_length=64, validators=[django.core.validators.RegexValidator(re.compile('^[-a-z0-9_]+\\Z'), 'Enter a valid value consisting of lowercase letters, numbers, underscores or hyphens.', 'invalid')], verbose_name='intent'),
),
migrations.RunPython(populate_empty_intent),
]
18 changes: 18 additions & 0 deletions bothub/common/migrations/0022_repositoryupdate_training_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-09-26 17:43

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0021_auto_20180921_1259'),
]

operations = [
migrations.AddField(
model_name='repositoryupdate',
name='training_log',
field=models.TextField(blank=True, editable=False, verbose_name='training log'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-10-02 17:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0022_repositoryupdate_training_log'),
]

operations = [
migrations.AddField(
model_name='repository',
name='use_language_model_featurizer',
field=models.BooleanField(default=True, help_text='You can use language featurizer to get words similarity. You need less examples to create a great bot.', verbose_name='Use language model featurizer'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-10-02 17:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0023_repository_use_language_model_featurizer'),
]

operations = [
migrations.AddField(
model_name='repositoryupdate',
name='use_language_model_featurizer',
field=models.BooleanField(default=True),
),
]
23 changes: 23 additions & 0 deletions bothub/common/migrations/0025_auto_20181003_1911.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.0.6 on 2018-10-03 19:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0024_repositoryupdate_use_language_model_featurizer'),
]

operations = [
migrations.AddField(
model_name='repository',
name='use_competing_intents',
field=models.BooleanField(default=False, help_text='When using competing intents the confidence of the prediction is distributed in all the intents.', verbose_name='Use competing intents'),
),
migrations.AddField(
model_name='repositoryupdate',
name='use_competing_intents',
field=models.BooleanField(default=False),
),
]
Loading

0 comments on commit e6afaf6

Please sign in to comment.