Skip to content

Commit 246afb5

Browse files
author
Val Brodsky
committed
Add labeling service dashboard getProjectById
1 parent 5eb500c commit 246afb5

File tree

5 files changed

+180
-35
lines changed

5 files changed

+180
-35
lines changed

libs/labelbox/src/labelbox/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from labelbox import utils
2121
from labelbox.adv_client import AdvClient
2222
from labelbox.orm import query
23+
from labelbox.orm.comparison import Comparison
2324
from labelbox.orm.db_object import DbObject
2425
from labelbox.orm.model import Entity, Field
2526
from labelbox.pagination import PaginatedCollection
@@ -56,6 +57,7 @@
5657
from labelbox.schema.label_score import LabelScore
5758
from labelbox.schema.ontology_kind import (OntologyKind, EditorTaskTypeMapper,
5859
EditorTaskType)
60+
from labelbox.schema.labeling_service_dashboard import LabelingServiceDashboard
5961

6062
logger = logging.getLogger(__name__)
6163

@@ -2389,3 +2391,10 @@ def upsert_label_feedback(self, label_id: str, feedback: str,
23892391
labelbox.LabelScore(name=x['name'], score=x['score'])
23902392
for x in scores_raw
23912393
]
2394+
2395+
def get_labeling_service_dashboards(
2396+
self,
2397+
from_cursor: Optional[str] = None,
2398+
where: Optional[Comparison] = None,
2399+
) -> PaginatedCollection:
2400+
return LabelingServiceDashboard.get_all(self, from_cursor, where)

libs/labelbox/src/labelbox/schema/labeling_service.py

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
11
from datetime import datetime
2-
from enum import Enum
32
from typing import Any
43
from typing_extensions import Annotated
54

65
from labelbox.exceptions import ResourceNotFoundError
76

87
from labelbox.pydantic_compat import BaseModel, Field
98
from labelbox.utils import _CamelCaseMixin
9+
from labelbox.schema.labeling_service_dashboard import LabelingServiceDashboard
10+
from labelbox.schema.labeling_service_status import LabelingServiceStatus
1011

1112
Cuid = Annotated[str, Field(min_length=25, max_length=25)]
1213

1314

14-
class LabelingServiceStatus(Enum):
15-
""""
16-
The status of the labeling service.
17-
"""
18-
Accepted = 'ACCEPTED'
19-
Calibration = 'CALIBRATION'
20-
Complete = 'COMPLETE'
21-
Production = 'PRODUCTION'
22-
Requested = 'REQUESTED'
23-
SetUp = 'SET_UP'
24-
25-
2615
class LabelingService(BaseModel):
2716
"""
2817
Labeling service for a project. This is a service that can be requested to label data for a project.
@@ -65,6 +54,34 @@ def start(cls, client, project_id: Cuid) -> 'LabelingService':
6554
raise Exception("Failed to start labeling service")
6655
return cls.get(client, project_id)
6756

57+
@classmethod
58+
def get(cls, client, project_id: Cuid) -> 'LabelingService':
59+
"""
60+
Returns the labeling service associated with the project.
61+
62+
Raises:
63+
ResourceNotFoundError: If the project does not have a labeling service.
64+
"""
65+
query = """
66+
query GetProjectBoostWorkforcePyApi($projectId: ID!) {
67+
projectBoostWorkforce(data: { projectId: $projectId }) {
68+
id
69+
projectId
70+
createdAt
71+
updatedAt
72+
createdById
73+
status
74+
}
75+
}
76+
"""
77+
result = client.execute(query, {"projectId": project_id})
78+
if result["projectBoostWorkforce"] is None:
79+
raise ResourceNotFoundError(
80+
message="The project does not have a labeling service.")
81+
data = result["projectBoostWorkforce"]
82+
data["client"] = client
83+
return LabelingService(**data)
84+
6885
def request(self) -> 'LabelingService':
6986
"""
7087
Creates a request to labeling service to start labeling for the project.
@@ -106,30 +123,11 @@ def getOrCreate(cls, client, project_id: Cuid) -> 'LabelingService':
106123
except ResourceNotFoundError:
107124
return cls.start(client, project_id)
108125

109-
@classmethod
110-
def get(cls, client, project_id: Cuid) -> 'LabelingService':
126+
def dashboard(self) -> LabelingServiceDashboard:
111127
"""
112-
Returns the labeling service associated with the project.
128+
Returns the dashboard for the labeling service associated with the project.
113129
114130
Raises:
115131
ResourceNotFoundError: If the project does not have a labeling service.
116132
"""
117-
query = """
118-
query GetProjectBoostWorkforcePyApi($projectId: ID!) {
119-
projectBoostWorkforce(data: { projectId: $projectId }) {
120-
id
121-
projectId
122-
createdAt
123-
updatedAt
124-
createdById
125-
status
126-
}
127-
}
128-
"""
129-
result = client.execute(query, {"projectId": project_id})
130-
if result["projectBoostWorkforce"] is None:
131-
raise ResourceNotFoundError(
132-
message="The project does not have a labeling service.")
133-
data = result["projectBoostWorkforce"]
134-
data["client"] = client
135-
return LabelingService(**data)
133+
return LabelingServiceDashboard.get(self.client, self.project_id)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from datetime import datetime
2+
from string import Template
3+
from typing import Any, Optional
4+
5+
from labelbox.exceptions import ResourceNotFoundError
6+
from labelbox.orm.comparison import Comparison
7+
from labelbox.pagination import PaginatedCollection
8+
from labelbox.pydantic_compat import BaseModel, root_validator
9+
from labelbox.utils import _CamelCaseMixin
10+
from labelbox.schema.labeling_service_status import LabelingServiceStatus
11+
12+
13+
class LabelingServiceDashboard(BaseModel):
14+
id: str
15+
name: str
16+
# service_type: str
17+
# created_at: datetime
18+
# updated_at: datetime
19+
# created_by_id: str
20+
status: LabelingServiceStatus
21+
tasks_completed: int
22+
tasks_remaining: int
23+
24+
client: Any # type Any to avoid circular import from client
25+
26+
def __init__(self, **kwargs):
27+
super().__init__(**kwargs)
28+
if not self.client.enable_experimental:
29+
raise RuntimeError(
30+
"Please enable experimental in client to use LabelingService")
31+
32+
class Config(_CamelCaseMixin.Config):
33+
...
34+
35+
@classmethod
36+
def get(cls, client, project_id: str) -> 'LabelingServiceDashboard':
37+
"""
38+
Returns the labeling service associated with the project.
39+
40+
Raises:
41+
ResourceNotFoundError: If the project does not have a labeling service.
42+
"""
43+
query = """
44+
query GetProjectByIdPyApi($id: ID!) {
45+
getProjectById(input: {id: $id}) {
46+
id
47+
name
48+
# serviceType
49+
# createdAt
50+
# updatedAt
51+
# createdById
52+
boostStatus
53+
dataRowsCount
54+
dataRowsInReviewCount
55+
dataRowsInReworkCount
56+
dataRowsDoneCount
57+
}
58+
}
59+
"""
60+
result = client.execute(query, {"id": project_id})
61+
if result["getProjectById"] is None:
62+
raise ResourceNotFoundError(
63+
message="The project does not have a labeling service.")
64+
data = result["getProjectById"]
65+
data["client"] = client
66+
return cls(**data)
67+
68+
@classmethod
69+
def get_all(
70+
cls,
71+
client,
72+
from_cursor: Optional[str] = None,
73+
where: Optional[Comparison] = None,
74+
) -> PaginatedCollection:
75+
page_size = 500 # hardcode to avoid overloading the server
76+
# where_param = query.where_as_dict(Entity.DataRow,
77+
# where) if where is not None else None
78+
79+
template = Template(
80+
"""query SearchProjectsPyApi($$id: ID!, $$after: ID, $$first: Int, $$where: SearchProjectsInput) {
81+
searchProjects(id: $$id, after: $$after, first: $$first, where: $$where)
82+
{
83+
nodes { $datarow_selections }
84+
pageInfo { hasNextPage startCursor }
85+
}
86+
}
87+
""")
88+
query_str = template.substitute(
89+
datarow_selections=LabelingServiceDashboard.schema()
90+
['properties'].keys())
91+
92+
params = {
93+
'id': self.uid,
94+
'from': from_cursor,
95+
'first': page_size,
96+
'where': where_param,
97+
}
98+
99+
return PaginatedCollection(
100+
client=client,
101+
query=query_str,
102+
params=params,
103+
dereferencing=['searchProjects', 'nodes'],
104+
obj_class=LabelingServiceDashboard,
105+
cursor_path=['datasetDataRows', 'pageInfo', 'endCursor'],
106+
)
107+
108+
@root_validator(pre=True)
109+
def convert_graphql_to_attrs(cls, data):
110+
if 'boostStatus' in data:
111+
data['status'] = LabelingServiceStatus(data.pop('boostStatus'))
112+
if 'dataRowsDoneCount' in data:
113+
data['tasksCompleted'] = data.pop('dataRowsDoneCount')
114+
if 'dataRowsCount' in data and 'dataRowsInReviewCount' in data and 'dataRowsInReworkCount' in data:
115+
data['tasksRemaining'] = data['dataRowsCount'] - (
116+
data['dataRowsInReviewCount'] + data['dataRowsInReworkCount'])
117+
118+
return data
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from enum import Enum
2+
3+
4+
class LabelingServiceStatus(Enum):
5+
Accepted = 'ACCEPTED'
6+
Calibration = 'CALIBRATION'
7+
Complete = 'COMPLETE'
8+
Production = 'PRODUCTION'
9+
Requested = 'REQUESTED'
10+
SetUp = 'SET_UP'

libs/labelbox/src/labelbox/schema/project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from urllib.parse import urlparse
1111

1212
from labelbox.schema.labeling_service import LabelingService, LabelingServiceStatus
13+
from labelbox.schema.labeling_service_dashboard import LabelingServiceDashboard
1314
import requests
1415

1516
from labelbox import parser
@@ -1941,6 +1942,15 @@ def get_labeling_service_status(self) -> LabelingServiceStatus:
19411942
"""
19421943
return self.get_labeling_service().status
19431944

1945+
@experimental
1946+
def labeling_service_dashboard(self) -> LabelingServiceDashboard:
1947+
"""Get the labeling service for this project.
1948+
1949+
Returns:
1950+
LabelingService: The labeling service for this project.
1951+
"""
1952+
return LabelingServiceDashboard.get(self.client, self.uid)
1953+
19441954

19451955
class ProjectMember(DbObject):
19461956
user = Relationship.ToOne("User", cache=True)

0 commit comments

Comments
 (0)