Skip to content

Commit 167742b

Browse files
committed
Fix req
2 parents 85a8653 + c3a370d commit 167742b

File tree

8 files changed

+114
-48
lines changed

8 files changed

+114
-48
lines changed

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
minversion = 3.0
33
log_cli=true
44
python_files = test_*.py
5-
;addopts = -n32 --dist=loadscope
5+
addopts = -n32 --dist=loadscope

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,7 @@ def upload_images_from_public_urls_to_project(
609609

610610
project_name, folder_name = extract_project_folder(project)
611611
existing_images = controller.get_duplicated_images(
612-
project_name=project_name,
613-
folder_name=folder_name,
614-
images=img_names,
612+
project_name=project_name, folder_name=folder_name, images=img_names,
615613
)
616614

617615
image_name_url_map = {}
@@ -1548,6 +1546,28 @@ def upload_images_from_folder_to_project(
15481546
"""
15491547

15501548
project_name, folder_name = extract_project_folder(project)
1549+
if recursive_subfolders:
1550+
logger.info(
1551+
"When using recursive subfolder parsing same name images in different subfolders will overwrite each other."
1552+
)
1553+
1554+
if not isinstance(extensions, (list, tuple)):
1555+
raise AppException(
1556+
"extensions should be a list or a tuple in upload_images_from_folder_to_project"
1557+
)
1558+
1559+
project_folder_name = project_name + (
1560+
f"/{folder_name}" if folder_name != "root" else ""
1561+
)
1562+
1563+
logger.info(
1564+
"Uploading all images with extensions %s from %s to project %s. Excluded file patterns are: %s.",
1565+
extensions,
1566+
folder_path,
1567+
project_folder_name,
1568+
exclude_file_patterns,
1569+
)
1570+
15511571
use_case = controller.upload_images_from_folder_to_project(
15521572
project_name=project_name,
15531573
folder_name=folder_name,
@@ -1559,7 +1579,14 @@ def upload_images_from_folder_to_project(
15591579
recursive_sub_folders=recursive_subfolders,
15601580
image_quality_in_editor=image_quality_in_editor,
15611581
)
1562-
images_to_upload, _ = use_case.images_to_upload
1582+
images_to_upload, duplicates = use_case.images_to_upload
1583+
if len(duplicates):
1584+
logger.warning(
1585+
"%s already existing images found that won't be uploaded.", len(duplicates)
1586+
)
1587+
logger.info(
1588+
"Uploading %s images to project %s.", len(images_to_upload), project_folder_name
1589+
)
15631590
if use_case.is_valid():
15641591
with tqdm(total=len(images_to_upload), desc="Uploading images") as progress_bar:
15651592
for _ in use_case.execute():
@@ -2061,6 +2088,9 @@ def create_annotation_classes_from_classes_json(
20612088
:rtype: list of dicts
20622089
"""
20632090
if not isinstance(classes_json, list):
2091+
logger.info(
2092+
"Creating annotation classes in project %s from %s.", project, classes_json,
2093+
)
20642094
if from_s3_bucket:
20652095
from_session = boto3.Session()
20662096
from_s3 = from_session.resource("s3")
@@ -2073,6 +2103,7 @@ def create_annotation_classes_from_classes_json(
20732103
annotation_classes = json.load(open(classes_json))
20742104
else:
20752105
annotation_classes = classes_json
2106+
20762107
response = controller.create_annotation_classes(
20772108
project_name=project, annotation_classes=annotation_classes,
20782109
)
@@ -2491,9 +2522,26 @@ def upload_annotations_from_folder_to_project(
24912522
"The function does not support projects containing videos attached with URLs"
24922523
)
24932524

2525+
if recursive_subfolders:
2526+
logger.info(
2527+
"When using recursive subfolder parsing same name annotations in different subfolders will overwrite each other.",
2528+
)
2529+
2530+
logger.info(
2531+
"The JSON files should follow specific naming convention. For Vector projects they should be named '<image_name>___objects.json', for Pixel projects JSON file should be names '<image_name>___pixel.json' and also second mask image file should be present with the name '<image_name>___save.png'. In both cases image with <image_name> should be already present on the platform."
2532+
)
2533+
logger.info("Existing annotations will be overwritten.",)
2534+
logger.info(
2535+
"Uploading all annotations from %s to project %s.", folder_path, project_name
2536+
)
2537+
24942538
annotation_paths = get_annotation_paths(
24952539
folder_path, from_s3_bucket, recursive_subfolders
24962540
)
2541+
logger.info(
2542+
"Uploading %s annotations to project %s.", len(annotation_paths), project_name
2543+
)
2544+
24972545
uploaded_annotations = []
24982546
failed_annotations = []
24992547
missing_annotations = []
@@ -2509,8 +2557,6 @@ def upload_annotations_from_folder_to_project(
25092557
annotation_paths=annotation_paths[i : i + chunk_size], # noqa: E203
25102558
client_s3_bucket=from_s3_bucket,
25112559
)
2512-
if response.errors:
2513-
logger.warning(response.errors)
25142560
if response.data:
25152561
uploaded_annotations.extend(response.data[0])
25162562
missing_annotations.extend(response.data[1])
@@ -3617,7 +3663,7 @@ def delete_annotations(project: str, image_names: List[str] = None):
36173663
project_name, folder_name = extract_project_folder(project)
36183664

36193665
response = controller.delete_annotations(
3620-
project_name=project, folder_name=folder_name, image_names=image_names
3666+
project_name=project_name, folder_name=folder_name, image_names=image_names
36213667
)
36223668
if response.errors:
36233669
raise AppException(response.errors)

src/superannotate/lib/core/enums.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ def get_value(cls, name):
2323
if enum.name.lower() == name.lower():
2424
return enum.value
2525

26+
@classmethod
27+
def values(cls):
28+
return [enum.name.lower() for enum in list(cls)]
29+
2630

2731
class ProjectType(BaseTitledEnum):
2832
VECTOR = "Vector", 1

src/superannotate/lib/core/usecases.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,8 @@ def execute(self):
682682
folder_id=self._folder.uuid,
683683
images=[image.name for image in self._attachments],
684684
)
685+
if isinstance(response, dict) and "error" in response:
686+
raise AppException(response["error"])
685687
duplications = [image["name"] for image in response]
686688
meta = {}
687689
to_upload = []
@@ -2445,7 +2447,7 @@ def execute(self):
24452447
)
24462448

24472449
if not response.ok:
2448-
self._response.errors = AppException(response.json()['error'])
2450+
self._response.errors = AppException(response.json()["error"])
24492451

24502452
in_progress = response.ok
24512453
if in_progress:
@@ -2725,11 +2727,7 @@ def execute(self):
27252727
unique_annotation_classes.append(annotation_class)
27262728

27272729
created = []
2728-
logger.info(
2729-
"Creating annotation classes in project %s from %s.",
2730-
self._project.name,
2731-
self._annotation_classes,
2732-
)
2730+
27332731
for i in range(0, len(unique_annotation_classes), self.CHUNK_SIZE):
27342732
created += self._service.set_annotation_classes(
27352733
project_id=self._project.uuid,
@@ -3423,9 +3421,10 @@ def execute(self):
34233421
filter(lambda detail: detail.id is not None, images_detail)
34243422
)
34253423
if missing_annotations:
3426-
self._response.errors = AppException(
3427-
f"Couldn't find image {','.join(map(lambda x: x.path, missing_annotations))} for annotation upload."
3428-
)
3424+
for missing in missing_annotations:
3425+
logger.warning(
3426+
f"Couldn't find image {missing.path} for annotation upload."
3427+
)
34293428

34303429
if self._pre_annotation:
34313430
auth_data = self._backend_service.get_pre_annotation_upload_data(
@@ -4302,6 +4301,16 @@ def exclude_file_patterns(self):
43024301
return constances.DEFAULT_FILE_EXCLUDE_PATTERNS
43034302
return self._exclude_file_patterns
43044303

4304+
def validate_annotation_status(self):
4305+
if self._annotation_status and self._annotation_status not in constances.AnnotationStatus.values():
4306+
raise AppValidationException("Invalid annotations status")
4307+
4308+
def validate_extensions(self):
4309+
if self._extensions and not all(
4310+
[extension in constances.DEFAULT_IMAGE_EXTENSIONS for extension in self._extensions]
4311+
):
4312+
raise AppValidationException("")
4313+
43054314
def validate_project_type(self):
43064315
if self._project.project_type == constances.ProjectType.VIDEO.value:
43074316
raise AppValidationException(
@@ -4510,7 +4519,7 @@ def execute(self) -> Response:
45104519

45114520
if self._folder.name == "root" and not self._image_names:
45124521
response = self._backend_service.delete_image_annotations(
4513-
project_id=self._project.uuid, team_id=self._project.team_id,
4522+
project_id=self._project.uuid, team_id=self._project.team_id, image_names=self._image_names
45144523
)
45154524
else:
45164525
response = self._backend_service.delete_image_annotations(
@@ -4536,21 +4545,22 @@ def execute(self) -> Response:
45364545
self._response.errors = "Annotations delete fails."
45374546
break
45384547
else:
4539-
logger.info(f"Annotations deleted")
4548+
logger.info("Annotations deleted")
45404549
break
45414550
else:
4542-
self._response.errors = AppException("Invalid image names.")
4551+
self._response.errors = AppException("Invalid image names or empty folder.")
45434552
return self._response
45444553

45454554

45464555
class GetDuplicateImages(BaseUseCase):
4547-
def __init__(self,
4548-
service: SuerannotateServiceProvider,
4549-
project_id :int,
4550-
team_id: int,
4551-
folder_id: int,
4552-
images: List[str]
4553-
):
4556+
def __init__(
4557+
self,
4558+
service: SuerannotateServiceProvider,
4559+
project_id: int,
4560+
team_id: int,
4561+
folder_id: int,
4562+
images: List[str],
4563+
):
45544564
super().__init__()
45554565
self._service = service
45564566
self._project_id = project_id
@@ -4566,8 +4576,7 @@ def execute(self):
45664576
project_id=self._project_id,
45674577
team_id=self._team_id,
45684578
folder_id=self._folder_id,
4569-
images=self._images[i: i + self._chunk_size],
4579+
images=self._images[i : i + self._chunk_size],
45704580
)
45714581
duplicates += [image["name"] for image in duplications]
45724582
return duplicates
4573-

src/superannotate/lib/infrastructure/controller.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -708,11 +708,12 @@ def get_project_workflow(self, project_name: str):
708708

709709
def search_annotation_classes(self, project_name: str, name_prefix: str = None):
710710
project_entity = self._get_project(project_name)
711+
condition = Condition("name", name_prefix, EQ) if name_prefix else None
711712
use_case = usecases.GetAnnotationClassesUseCase(
712713
classes=AnnotationClassRepository(
713714
service=self._backend_client, project=project_entity
714715
),
715-
condition=Condition("name", name_prefix, EQ),
716+
condition=condition
716717
)
717718
return use_case.execute()
718719

@@ -1471,15 +1472,16 @@ def delete_annotations(
14711472
)
14721473
return use_case.execute()
14731474

1474-
1475-
def get_duplicated_images(self,project_name: str ,folder_name :str ,images: List[str]):
1475+
def get_duplicated_images(
1476+
self, project_name: str, folder_name: str, images: List[str]
1477+
):
14761478
project = self._get_project(project_name)
14771479
folder = self._get_folder(project, folder_name)
14781480
use_case = usecases.GetDuplicateImages(
14791481
service=self._backend_client,
14801482
project_id=project.uuid,
1481-
team_id= project.team_id,
1482-
folder_id= folder.uuid,
1483-
images=images
1483+
team_id=project.team_id,
1484+
folder_id=folder.uuid,
1485+
images=images,
14841486
)
14851487
return use_case.execute()

src/superannotate/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "5.0.0b18"
1+
__version__ = "5.0.0b19"

tests/integration/test_annotation_delete.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class TestAnnotationDelete(BaseTestCase):
1010
PROJECT_NAME = "TestAnnotationDelete"
1111
PROJECT_DESCRIPTION = "desc"
1212
PROJECT_TYPE = "Vector"
13+
TEST_FOLDER_NAME = "folder"
1314
TEST_FOLDER_PATH = "data_set/sample_project_vector_single_image"
1415
EXAMPLE_IMAGE_1 = "example_image_1.jpg"
1516
EXAMPLE_IMAGE_2 = "example_image_2.jpg"
@@ -48,7 +49,7 @@ def test_delete_annotations(self):
4849
@pytest.mark.skip(
4950
"waiting for deployment to dev",
5051
)
51-
def test_delete_annotations_by_not_exsisting_name(self):
52+
def test_delete_annotations_by_not_existing_name(self):
5253
sa.upload_images_from_folder_to_project(
5354
self.PROJECT_NAME, self.folder_path, annotation_status="InProgress"
5455
)
@@ -59,3 +60,4 @@ def test_delete_annotations_by_not_exsisting_name(self):
5960
self.PROJECT_NAME, f"{self.folder_path}"
6061
)
6162
self.assertRaises(Exception, sa.delete_annotations, self.PROJECT_NAME, [self.EXAMPLE_IMAGE_2])
63+

tests/profiling/profiling.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,19 @@
3232
# print(results)
3333
# print(sum(results)/10)
3434

35-
import time
36-
results = []
37-
projects = "11", "22", "33", "44", "55", "66", "77", "88", "99"
38-
39-
for project in projects:
40-
start = time.time()
41-
sa.search_annotation_classes(project)
42-
end = time.time()
43-
results.append(end-start)
35+
# import time
36+
# results = []
37+
# projects = "11", "22", "33", "44", "55", "66", "77", "88", "99"
38+
#
39+
# for project in projects:
40+
# start = time.time()
41+
# sa.search_annotation_classes(project)
42+
# end = time.time()
43+
# results.append(end-start)
44+
#
45+
# print(results)
46+
# print(sum(results)/9)
4447

45-
print(results)
46-
print(sum(results)/9)
48+
from src.superannotate.lib.core.enums import AnnotationStatus
4749

50+
pass

0 commit comments

Comments
 (0)