Skip to content

Commit

Permalink
Merge branch 'develop' into 'fb-optic-1178/memory-leak'
Browse files Browse the repository at this point in the history
  • Loading branch information
yyassi-heartex committed Dec 16, 2024
2 parents 6f14092 + b876970 commit 82c9232
Show file tree
Hide file tree
Showing 17 changed files with 172 additions and 19 deletions.
6 changes: 6 additions & 0 deletions label_studio/core/all_urls.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@
"name": "projects:api:project-detail",
"decorators": ""
},
{
"url": "/api/projects/counts/",
"module": "projects.api.ProjectCountsListAPI",
"name": "projects:api:project-counts-list",
"decorators": ""
},
{
"url": "/api/projects/<int:pk>/next/",
"module": "projects.api.ProjectNextTaskAPI",
Expand Down
31 changes: 29 additions & 2 deletions label_studio/feature_flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,33 @@
"version": 4,
"deleted": false
},
"fflag_feat_front_dia_1747_projects_list_banner": {
"key": "fflag_feat_front_dia_1747_projects_list_banner",
"on": false,
"prerequisites": [],
"targets": [],
"contextTargets": [],
"rules": [],
"fallthrough": {
"variation": 0
},
"offVariation": 1,
"variations": [
true,
false
],
"clientSideAvailability": {
"usingMobileKey": false,
"usingEnvironmentId": false
},
"clientSide": false,
"salt": "21bfa474c82b4ab0b89314fbecd164cc",
"trackEvents": false,
"trackEventsFallthrough": false,
"debugEventsUntilDate": null,
"version": 3,
"deleted": false
},
"fflag_feat_front_dia_916_model_creation_and_versioning_short": {
"key": "fflag_feat_front_dia_916_model_creation_and_versioning_short",
"on": true,
Expand Down Expand Up @@ -3036,7 +3063,7 @@
"trackEvents": false,
"trackEventsFallthrough": false,
"debugEventsUntilDate": null,
"version": 2,
"version": 3,
"deleted": false
},
"fflag_feat_front_optic_1419_backend_csv_comments_export_short": {
Expand Down Expand Up @@ -4778,7 +4805,7 @@
"trackEvents": false,
"trackEventsFallthrough": false,
"debugEventsUntilDate": null,
"version": 2,
"version": 3,
"deleted": false
},
"fflag_fix_leap_246_multi_object_hotkeys_160124_short": {
Expand Down
31 changes: 31 additions & 0 deletions label_studio/projects/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from projects.models import Project, ProjectImport, ProjectManager, ProjectReimport, ProjectSummary
from projects.serializers import (
GetFieldsSerializer,
ProjectCountsSerializer,
ProjectImportSerializer,
ProjectLabelConfigSerializer,
ProjectModelVersionExtendedSerializer,
Expand Down Expand Up @@ -281,6 +282,36 @@ def post(self, request, *args, **kwargs):
return super(ProjectListAPI, self).post(request, *args, **kwargs)


@method_decorator(
name='get',
decorator=swagger_auto_schema(
tags=['Projects'],
x_fern_sdk_group_name='projects',
x_fern_sdk_method_name='counts',
x_fern_audiences=['public'],
x_fern_pagination={
'offset': '$request.page',
'results': '$response.results',
},
operation_summary="List project's counts",
operation_description='Returns a list of projects with their counts. For example, task_number which is the total task number in project',
),
)
class ProjectCountsListAPI(generics.ListAPIView):
serializer_class = ProjectCountsSerializer
filterset_class = ProjectFilterSet
permission_required = ViewClassPermission(
GET=all_permissions.projects_view,
)
pagination_class = ProjectListPagination

def get_queryset(self):
serializer = GetFieldsSerializer(data=self.request.query_params)
serializer.is_valid(raise_exception=True)
fields = serializer.validated_data.get('include')
return Project.objects.with_counts(fields=fields).filter(organization=self.request.user.active_organization)


@method_decorator(
name='get',
decorator=swagger_auto_schema(
Expand Down
16 changes: 16 additions & 0 deletions label_studio/projects/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,22 @@ def get_queue_done(self, project):
return result


class ProjectCountsSerializer(ProjectSerializer):
class Meta:
model = Project
fields = [
'id',
'task_number',
'finished_task_number',
'total_predictions_number',
'total_annotations_number',
'num_tasks_with_annotations',
'useful_annotation_number',
'ground_truth_number',
'skipped_annotations_number',
]


class ProjectOnboardingSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectOnboarding
Expand Down
1 change: 1 addition & 0 deletions label_studio/projects/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# CRUD
path('', api.ProjectListAPI.as_view(), name='project-list'),
path('<int:pk>/', api.ProjectAPI.as_view(), name='project-detail'),
path('counts/', api.ProjectCountsListAPI.as_view(), name='project-counts-list'),
# Get next task
path('<int:pk>/next/', api.ProjectNextTaskAPI.as_view(), name='project-next'),
# Label stream history
Expand Down
49 changes: 49 additions & 0 deletions label_studio/tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import pytest
from django.db.models.query import QuerySet
from django.test import TestCase
from django.urls import reverse
from django.utils.http import urlencode
from organizations.models import Organization
from projects.models import Project
from rest_framework.test import APIClient
from tasks.models import Task
from tests.utils import make_project
from users.models import User

Expand Down Expand Up @@ -40,3 +47,45 @@ def test_project_all_members(business_client):

assert isinstance(members, QuerySet)
assert isinstance(members.first(), User)


class TestProjectCountsListAPI(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(username='testuser', email='[email protected]', password='testpassword')
cls.organization = Organization.objects.create(title='testorganization')
cls.user.active_organization = cls.organization
cls.user.save()
cls.project_1 = Project.objects.create(title='Project 1', organization=cls.organization)
cls.project_2 = Project.objects.create(title='Project 2', organization=cls.organization)
Task.objects.create(project=cls.project_1, data={'text': 'Task 1'})
Task.objects.create(project=cls.project_1, data={'text': 'Task 2'})
Task.objects.create(project=cls.project_2, data={'text': 'Task 3'})

def get_url(self, **params):
return f'{reverse("projects:api:project-counts-list")}?{urlencode(params)}'

def test_get_counts(self):

client = APIClient()
client.force_authenticate(user=self.user)
response = client.get(self.get_url(include='id,task_number,finished_task_number,total_predictions_number'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['count'], 2)
self.assertEqual(
response.json()['results'],
[
{
'id': self.project_1.id,
'task_number': 2,
'finished_task_number': 0,
'total_predictions_number': 0,
},
{
'id': self.project_2.id,
'task_number': 1,
'finished_task_number': 0,
'total_predictions_number': 0,
},
],
)
2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/814.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/814.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/main.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/vendor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/dist/apps/labelstudio/vendor.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions web/dist/apps/labelstudio/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "chore: OPTIC-1337: Update tooltips (#6737)",
"commit": "1747ab0387b57ca5eb91e31288f25e69ff905630",
"date": "2024-12-11T13:08:10.000Z",
"message": "fix: OPTIC-1418: Improve Organization Membership API usage to reduce latency (#6780)",
"commit": "ca1da7a2dc34dfd512d3d1dd71bf722ef4019f66",
"date": "2024-12-16T15:03:14.000Z",
"branch": "develop"
}
6 changes: 3 additions & 3 deletions web/dist/libs/datamanager/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "chore: OPTIC-1337: Update tooltips (#6737)",
"commit": "1747ab0387b57ca5eb91e31288f25e69ff905630",
"date": "2024-12-11T13:08:10.000Z",
"message": "fix: OPTIC-1418: Improve Organization Membership API usage to reduce latency (#6780)",
"commit": "ca1da7a2dc34dfd512d3d1dd71bf722ef4019f66",
"date": "2024-12-16T15:03:14.000Z",
"branch": "develop"
}
6 changes: 3 additions & 3 deletions web/dist/libs/editor/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"message": "chore: OPTIC-1337: Update tooltips (#6737)",
"commit": "1747ab0387b57ca5eb91e31288f25e69ff905630",
"date": "2024-12-11T13:08:10.000Z",
"message": "fix: OPTIC-1418: Improve Organization Membership API usage to reduce latency (#6780)",
"commit": "ca1da7a2dc34dfd512d3d1dd71bf722ef4019f66",
"date": "2024-12-16T15:03:14.000Z",
"branch": "develop"
}
2 changes: 1 addition & 1 deletion web/libs/datamanager/src/stores/AppStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ export const AppStore = types
}),

fetchUsers: flow(function* () {
const list = yield self.apiCall("users");
const list = yield self.apiCall("users", { __useQueryCache: 60 * 1000 });

self.users.push(...list);
}),
Expand Down
25 changes: 24 additions & 1 deletion web/libs/datamanager/src/utils/api-proxy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,26 @@ export class APIProxy {
let responseMeta;
const alwaysExpectJSON = options?.alwaysExpectJSON === undefined ? true : options.alwaysExpectJSON;

let shouldUseQueryCache = false;
try {
const finalParams = {
...(methodSettings.params ?? {}),
...(urlParams ?? {}),
...(this.sharedParams ?? {}),
};

if (finalParams.__useQueryCache && methodSettings.queryCache) {
shouldUseQueryCache = true;

const cachedData = methodSettings.queryCache(finalParams);

if (cachedData) {
return cachedData;
}

delete finalParams.__useQueryCache;
}

const { method, url: apiCallURL } = this.createUrl(
methodSettings.path,
finalParams,
Expand Down Expand Up @@ -247,7 +260,13 @@ export class APIProxy {
: { ok: true };

if (methodSettings.convert instanceof Function) {
return await methodSettings.convert(responseData);
const convertedData = await methodSettings.convert(responseData);

if (shouldUseQueryCache) {
methodSettings.queryCache(finalParams, convertedData);
}

return convertedData;
}

responseResult = responseData;
Expand All @@ -268,6 +287,10 @@ export class APIProxy {
writable: false,
});

if (shouldUseQueryCache) {
methodSettings.queryCache(finalParams, responseResult);
}

return responseResult;
};
}
Expand Down

0 comments on commit 82c9232

Please sign in to comment.