Skip to content

Commit

Permalink
Implemented a couple of rest methods.
Browse files Browse the repository at this point in the history
users/self
users/
users/<int:pk>
tasks/<int:pk>/frames/<int:frame>
exceptions/
  • Loading branch information
Nikita Manovich committed Jan 23, 2019
1 parent 0ca1ef4 commit 1b97f3c
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 59 deletions.
3 changes: 2 additions & 1 deletion cvat/apps/engine/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class dotdict(dict):

clogger = dotdict({
'task': TaskClientLoggerStorage(),
'job': JobClientLoggerStorage()
'job': JobClientLoggerStorage(),
'glob': logging.getLogger('cvat.client'),
})

slogger = dotdict({
Expand Down
27 changes: 18 additions & 9 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@ class Meta:
'bug_tracker', 'created_date', 'updated_date', 'overlap',
'z_order', 'flipped', 'source', 'status')

class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('name',)

class UserSerializer(serializers.ModelSerializer):
groups = GroupSerializer(many=True)
groups = serializers.SlugRelatedField(many=True,
slug_field='name', queryset=Group.objects.all())

class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'email', 'is_staff',
'is_superuser', 'is_active', 'groups')
write_only_fields = ('password', 'date_joined')
fields = ('id', 'username', 'first_name', 'last_name', 'email',
'groups', 'is_staff', 'is_superuser', 'is_active', 'last_login',
'date_joined', 'groups')
read_only_fields = ('last_login', 'date_joined')
write_only_fields = ('password', )

class ExceptionSerializer(serializers.Serializer):
task = serializers.IntegerField()
job = serializers.IntegerField()
message = serializers.CharField(max_length=1000)
filename = serializers.URLField()
line = serializers.IntegerField()
column = serializers.IntegerField()
stack = serializers.CharField(max_length=10000)
browser = serializers.CharField(max_length=255)
os = serializers.CharField(max_length=255)
53 changes: 24 additions & 29 deletions cvat/apps/engine/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,29 @@
REST_API_PREFIX = 'api/<version>/'

urlpatterns = [
path( # entry point for API
REST_API_PREFIX,
views.api_root,
name='root'),
# entry point for API
path(REST_API_PREFIX, views.api_root, name='root'),
# GET current active user
path(REST_API_PREFIX + 'users/self', views.UserSelf.as_view(),
name='user-self'),
# GET list of users, POST a new user
path(REST_API_PREFIX + 'users/', views.UserList.as_view(),
name='user-list'),
# GET, DELETE, PATCH the user
path(REST_API_PREFIX + 'users/<int:pk>', views.UserDetail.as_view(),
name='user-detail'),
# GET a frame for a specific task
path(REST_API_PREFIX + 'tasks/<int:pk>/frames/<int:frame>',
views.get_frame, name='task-frame'),
# POST an exception
path(REST_API_PREFIX + 'exceptions/', views.ClientException.as_view(),
name='exception-list'),


# GET information about the backend
path(REST_API_PREFIX + 'info/', views.dummy_view, name='server-info'),


path( # GET, POST
REST_API_PREFIX + 'tasks/',
views.TaskList.as_view(),
Expand All @@ -21,10 +40,6 @@
REST_API_PREFIX + 'tasks/<int:pk>',
views.TaskDetail.as_view(),
name='task-detail'),
path( # GET
REST_API_PREFIX + 'tasks/<int:pk>/frames/<int:frame>',
views.dummy_view,
name='task-frame'),
path( # GET
REST_API_PREFIX + 'tasks/<int:pk>/jobs/',
views.dummy_view,
Expand All @@ -41,26 +56,6 @@
REST_API_PREFIX + 'jobs/<int:pk>/annotations/',
views.dummy_view,
name='job-annotations'),
path( # GET
REST_API_PREFIX + 'users/',
views.UserList.as_view(),
name='user-list'),
path( # GET, DELETE, PATCH
REST_API_PREFIX + 'users/<int:pk>',
views.UserDetail.as_view(),
name='user-detail'),
path( # GET
REST_API_PREFIX + 'users/myself',
views.dummy_view,
name='user-myself'),
path( # POST
REST_API_PREFIX + 'exceptions/',
views.dummy_view,
name='exception-list'),
path( # GET
REST_API_PREFIX + 'info/',
views.dummy_view,
name='server-info'),
path( # GET
REST_API_PREFIX + 'plugins/',
views.dummy_view,
Expand All @@ -87,7 +82,7 @@
name='plugin-request-detail'),

path('create/task', views.create_task), ####
path('get/task/<int:tid>/frame/<int:frame>', views.get_frame), ###
path('get/task/<int:pk>/frame/<int:frame>', views.get_frame), ###
path('check/task/<int:tid>', views.check_task), ####
path('delete/task/<int:tid>', views.delete_task), ####
path('update/task/<int:tid>', views.update_task), ####
Expand Down
73 changes: 53 additions & 20 deletions cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from rest_framework.decorators import api_view, APIView
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.renderers import JSONRenderer



from . import annotation, task, models
Expand All @@ -26,7 +28,8 @@
import logging
from .log import slogger, clogger
from cvat.apps.engine.models import StatusChoice, Task
from cvat.apps.engine.serializers import TaskSerializer, UserSerializer
from cvat.apps.engine.serializers import TaskSerializer, UserSerializer,\
ExceptionSerializer
from django.contrib.auth.models import User

# Server REST API
Expand All @@ -37,7 +40,7 @@ def api_root(request, version=None):
return Response({
'tasks': reverse('task-list', request=request),
'users': reverse('user-list', request=request),
'myself': reverse('user-myself', request=request),
'myself': reverse('user-self', request=request),
'exceptions': reverse('exception-list', request=request),
'info': reverse('server-info', request=request),
'plugins': reverse('plugin-list', request=request)
Expand All @@ -48,28 +51,75 @@ class TaskList(generics.ListCreateAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer


class TaskDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Task.objects.all()
serializer_class = TaskSerializer


class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer


class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer


class UserSelf(generics.RetrieveAPIView):
serializer_class = UserSerializer

def get_object(self):
return self.request.user


@login_required
@permission_required(perm=['engine.task.access'],
fn=objectgetter(models.Task, 'pk'), raise_exception=True)
def get_frame(request, pk, frame, version=None):
"""Stream corresponding from for the task"""

try:
# Follow symbol links if the frame is a link on a real image otherwise
# mimetype detection inside sendfile will work incorrectly.
path = os.path.realpath(task.get_frame_path(pk, frame))
return sendfile(request, path)
except Exception as e:
slogger.task[pk].error(
"cannot get frame #{}".format(frame), exc_info=True)
return HttpResponseBadRequest(str(e))


class ClientException(APIView):
def post(self, request):
serializer = ExceptionSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
message = JSONRenderer().render(serializer.data)
jid = serializer.data["job"]
tid = serializer.data["task"]
if jid:
clogger.job[jid].error(message)
elif tid:
clogger.task[tid].error(message)
else:
clogger.glob.error(message)

return Response(serializer.data, status=status.HTTP_201_CREATED)


@api_view(['GET'])
def dummy_view(request, version=None, pk=None, frame=None, id=None, name=None):
return Response()


# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# High Level server API


@login_required
@permission_required(perm=['engine.job.access'],
fn=objectgetter(models.Job, 'jid'), raise_exception=True)
fn=objectgetter(models.Job, 'jid'), raise_exception=True)
def catch_client_exception(request, jid):
data = json.loads(request.body.decode('utf-8'))
for event in data['exceptions']:
Expand Down Expand Up @@ -169,23 +219,6 @@ def check_task(request, tid):
return JsonResponse(response)


@login_required
@permission_required(perm=['engine.task.access'],
fn=objectgetter(models.Task, 'tid'), raise_exception=True)
def get_frame(request, tid, frame):
"""Stream corresponding from for the task"""

try:
# Follow symbol links if the frame is a link on a real image otherwise
# mimetype detection inside sendfile will work incorrectly.
path = os.path.realpath(task.get_frame_path(tid, frame))
return sendfile(request, path)
except Exception as e:
slogger.task[tid].error(
"cannot get frame #{}".format(frame), exc_info=True)
return HttpResponseBadRequest(str(e))


@login_required
@permission_required(perm=['engine.task.delete'],
fn=objectgetter(models.Task, 'tid'), raise_exception=True)
Expand Down

0 comments on commit 1b97f3c

Please sign in to comment.