Skip to content

Commit c5e6b4a

Browse files
committed
Copy images
1 parent 0a4639a commit c5e6b4a

File tree

5 files changed

+168
-47
lines changed

5 files changed

+168
-47
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"python.formatting.provider": "yapf",
3-
"python.linting.pylintPath": "~/miniconda3/bin/pylint",
3+
"python.linting.pylintPath": "venv_sa_conv/bin/pylint",
44
"python.linting.pylintEnabled": true,
55
"python.testing.pytestArgs": [
66
"tests"

superannotate/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,18 @@ def consensus(*args, **kwargs):
4747
add_annotation_cuboid_to_image, add_annotation_ellipse_to_image,
4848
add_annotation_point_to_image, add_annotation_polygon_to_image,
4949
add_annotation_polyline_to_image, add_annotation_template_to_image,
50-
create_fuse_image, delete_image, delete_images, download_image,
51-
download_image_annotations, download_image_preannotations,
52-
get_image_annotations, get_image_bytes, get_image_metadata,
53-
get_image_preannotations, search_images, set_image_annotation_status,
54-
upload_image_annotations
50+
create_fuse_image, delete_image, download_image, download_image_annotations,
51+
download_image_preannotations, get_image_annotations, get_image_bytes,
52+
get_image_metadata, get_image_preannotations, search_images,
53+
set_image_annotation_status, upload_image_annotations
5554
)
5655
from .db.project_api import (
5756
create_folder, delete_folders, get_folder_metadata,
5857
get_project_and_folder_metadata, rename_folder, search_folders
5958
)
6059
from .db.project_images import (
61-
assign_images, copy_image, move_image, pin_image, upload_image_to_project
60+
assign_images, copy_image, copy_images, delete_images, move_image,
61+
move_images, pin_image, upload_image_to_project
6262
)
6363
from .db.project_metadata import get_project_metadata
6464
from .db.projects import (

superannotate/db/images.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -587,34 +587,6 @@ def delete_image(project, image_name):
587587
logger.info("Successfully deleted image %s.", image_name)
588588

589589

590-
def delete_images(project, image_names):
591-
"""Delete images in project.
592-
593-
:param project: project name or metadata of the project to be deleted
594-
:type project: str or dict
595-
:param folder_names: to be deleted folders' names
596-
:type folder_names: str or list of strs
597-
"""
598-
if not isinstance(image_names, list):
599-
raise SABaseException(0, "image_names should be a list of strs")
600-
images = get_image_metadata(project, image_names)
601-
project, _ = get_project_and_folder_metadata(project)
602-
603-
params = {"team_id": project["team_id"], "project_id": project["id"]}
604-
data = {"image_ids": [image["id"] for image in images]}
605-
response = _api.send_request(
606-
req_type='PUT',
607-
path='/image/delete/images',
608-
params=params,
609-
json_req=data
610-
)
611-
if not response.ok:
612-
raise SABaseException(
613-
response.status_code, "Couldn't delete images " + response.text
614-
)
615-
logger.info("Images %s deleted in project %s", image_names, project["name"])
616-
617-
618590
def get_image_bytes(project, image_name, variant='original'):
619591
"""Returns an io.BytesIO() object of the image. Suitable for creating
620592
PIL.Image out of it.

superannotate/db/project_images.py

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,22 @@ def upload_image_to_project(
133133
break
134134

135135

136-
def copy_images(project, source_folder, destination_folder, image_names):
137-
project, source_project_folder = get_project_and_folder_metadata(
138-
source_project
139-
)
140-
destination_project, destination_project_folder = get_project_and_folder_metadata(
141-
destination_project
142-
)
136+
def _copy_images(source_project, destination_project, image_names=None):
137+
source_project, source_project_folder = source_project
138+
destination_project, destination_project_folder = destination_project
139+
if source_project["id"] != destination_project["id"]:
140+
raise SABaseException(
141+
0,
142+
"Source and destination projects should be the same for copy_images"
143+
)
143144
params = {
144-
"team_id": _api.team_id,
145-
"project_id": img_metadatas[0]["project_id"]
145+
"team_id": source_project["team_id"],
146+
"project_id": source_project["id"]
146147
}
147148
json_req = {
148-
"image_ids": [x["id"] for x in img_metadatas],
149-
"destination_folder_id": destination_project_folder_id
149+
"image_names": image_names,
150+
"destination_folder_id": destination_project_folder["id"],
151+
"source_folder_name": source_project_folder["name"]
150152
}
151153
response = _api.send_request(
152154
req_type='POST', path='/image/copy', params=params, json_req=json_req
@@ -156,6 +158,85 @@ def copy_images(project, source_folder, destination_folder, image_names):
156158
response.status_code, "Couldn't copy images " + response.text
157159
)
158160

161+
return response.json()
162+
163+
164+
def copy_images(source_project, destination_project, image_names=None):
165+
source_project, source_project_folder = get_project_and_folder_metadata(
166+
source_project
167+
)
168+
destination_project, destination_project_folder = get_project_and_folder_metadata(
169+
destination_project
170+
)
171+
if image_names is None:
172+
image_names = search_images((source_project, source_project_folder))
173+
res = _copy_images(
174+
(source_project, source_project_folder),
175+
(destination_project, destination_project_folder), image_names
176+
)
177+
logger.info(
178+
"Copied images %s from %s to %s. Number of skipped images %s",
179+
image_names,
180+
source_project["name"] + "" if source_project_folder is None else "/" +
181+
source_project_folder["name"], destination_project["name"] +
182+
"" if destination_project_folder is None else "/" +
183+
destination_project_folder["name"], res["skipped"]
184+
)
185+
return res["skipped"]
186+
187+
188+
def delete_images(project, image_names):
189+
"""Delete images in project.
190+
191+
:param project: project name or metadata of the project to be deleted
192+
:type project: str or dict
193+
:param folder_names: to be deleted folders' names
194+
:type folder_names: str or list of strs
195+
"""
196+
if not isinstance(image_names, list):
197+
raise SABaseException(0, "image_names should be a list of strs")
198+
images = get_image_metadata(
199+
project, image_names, return_dict_on_single_output=False
200+
)
201+
project, _ = get_project_and_folder_metadata(project)
202+
203+
params = {"team_id": project["team_id"], "project_id": project["id"]}
204+
data = {"image_ids": [image["id"] for image in images]}
205+
response = _api.send_request(
206+
req_type='PUT',
207+
path='/image/delete/images',
208+
params=params,
209+
json_req=data
210+
)
211+
if not response.ok:
212+
raise SABaseException(
213+
response.status_code, "Couldn't delete images " + response.text
214+
)
215+
logger.info("Images %s deleted in project %s", image_names, project["name"])
216+
217+
218+
def move_images(source_project, destination_project, image_names=None):
219+
source_project, source_project_folder = get_project_and_folder_metadata(
220+
source_project
221+
)
222+
destination_project, destination_project_folder = get_project_and_folder_metadata(
223+
destination_project
224+
)
225+
if image_names is None:
226+
image_names = search_images((source_project, source_project_folder))
227+
_copy_images(
228+
(source_project, source_project_folder),
229+
(destination_project, destination_project_folder), image_names
230+
)
231+
delete_images((source_project, source_project_folder), image_names)
232+
logger.info(
233+
"Moved images %s from project %s to project %s", image_names,
234+
source_project["name"] + "" if source_project_folder is None else "/" +
235+
source_project_folder["name"], destination_project["name"] +
236+
"" if destination_project_folder is None else "/" +
237+
destination_project_folder["name"]
238+
)
239+
159240

160241
def copy_image(
161242
source_project,

tests/test_folders.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,72 @@ def test_delete_images(tmpdir):
231231
sa.delete_images(project, ["example_image_2.jpg", "example_image_3.jpg"])
232232

233233
num_images = sa.get_project_image_count(project)
234-
assert num_images == 2
234+
assert num_images == 2
235+
236+
237+
def test_copy_images(tmpdir):
238+
PROJECT_NAME = "test copy folder images"
239+
tmpdir = Path(tmpdir)
240+
241+
projects_found = sa.search_projects(PROJECT_NAME, return_metadata=True)
242+
for pr in projects_found:
243+
sa.delete_project(pr)
244+
245+
project = sa.create_project(PROJECT_NAME, 'test', 'Vector')
246+
sa.create_folder(project, "folder1")
247+
project = PROJECT_NAME + "/folder1"
248+
sa.upload_images_from_folder_to_project(
249+
project, FROM_FOLDER, annotation_status="InProgress"
250+
)
251+
num_images = sa.get_project_image_count(project)
252+
assert num_images == 4
253+
254+
sa.create_folder(PROJECT_NAME, "folder2")
255+
project2 = PROJECT_NAME + "/folder2"
256+
num_images = sa.get_project_image_count(project2)
257+
assert num_images == 0
258+
259+
sa.copy_images(
260+
project, project2, ["example_image_2.jpg", "example_image_3.jpg"]
261+
)
262+
263+
num_images = sa.get_project_image_count(project2)
264+
assert num_images == 2
265+
266+
res = sa.copy_images(project, project2, None)
267+
268+
num_images = sa.get_project_image_count(project2)
269+
assert num_images == 4
270+
271+
assert res == 2
272+
273+
274+
def test_move_images(tmpdir):
275+
PROJECT_NAME = "test move folder images"
276+
tmpdir = Path(tmpdir)
277+
278+
projects_found = sa.search_projects(PROJECT_NAME, return_metadata=True)
279+
for pr in projects_found:
280+
sa.delete_project(pr)
281+
282+
project = sa.create_project(PROJECT_NAME, 'test', 'Vector')
283+
sa.create_folder(project, "folder1")
284+
project = PROJECT_NAME + "/folder1"
285+
sa.upload_images_from_folder_to_project(
286+
project, FROM_FOLDER, annotation_status="InProgress"
287+
)
288+
num_images = sa.get_project_image_count(project)
289+
assert num_images == 4
290+
291+
sa.create_folder(PROJECT_NAME, "folder2")
292+
project2 = PROJECT_NAME + "/folder2"
293+
num_images = sa.get_project_image_count(project2)
294+
assert num_images == 0
295+
296+
sa.move_images(project, project2, ["example_image_2.jpg"])
297+
298+
num_images = sa.get_project_image_count(project2)
299+
assert num_images == 1
300+
301+
num_images = sa.get_project_image_count(project)
302+
assert num_images == 3

0 commit comments

Comments
 (0)