|
8 | 8 | from multiprocessing.pool import Pool, ThreadPool |
9 | 9 | from threading import Lock |
10 | 10 | from typing import Dict, Generator, List, Optional, Set, TextIO, Tuple, Union |
11 | | -from urllib.parse import urlparse |
12 | 11 | from uuid import uuid4 |
13 | 12 |
|
14 | 13 | import backoff |
@@ -645,6 +644,86 @@ def trigger_images_references_ingest( |
645 | 644 | raise RFAPICallError("Could not decode Roboflow API response.") from error |
646 | 645 |
|
647 | 646 |
|
| 647 | +def create_images_batch_from_roboql_query( |
| 648 | + query: str, |
| 649 | + batch_id: str, |
| 650 | + api_key: str, |
| 651 | + ingest_id: Optional[str] = None, |
| 652 | + batch_name: Optional[str] = None, |
| 653 | + notifications_url: Optional[str] = None, |
| 654 | +) -> None: |
| 655 | + workspace = get_workspace(api_key=api_key) |
| 656 | + trigger_roboql_batch_ingest( |
| 657 | + workspace=workspace, |
| 658 | + batch_id=batch_id, |
| 659 | + query=query, |
| 660 | + api_key=api_key, |
| 661 | + ingest_id=ingest_id, |
| 662 | + batch_name=batch_name, |
| 663 | + notifications_url=notifications_url, |
| 664 | + ) |
| 665 | + print( |
| 666 | + f"RoboQL ingest accepted for batch '{batch_id}'. The batch is materialised " |
| 667 | + "asynchronously by the data-staging worker — initial counts will be zero " |
| 668 | + "until the worker registers shards." |
| 669 | + ) |
| 670 | + if notifications_url: |
| 671 | + print(f"Monitor updates that will be sent to: {notifications_url}") |
| 672 | + print( |
| 673 | + f"Use `inference rf-cloud data-staging show-batch-details --batch-id {batch_id}` " |
| 674 | + "to inspect the batch once it appears, and " |
| 675 | + f"`inference rf-cloud data-staging list-batch-content --batch-id {batch_id}` " |
| 676 | + "to list its files once the worker has staged them." |
| 677 | + ) |
| 678 | + |
| 679 | + |
| 680 | +@backoff.on_exception( |
| 681 | + backoff.constant, |
| 682 | + exception=RetryError, |
| 683 | + max_tries=3, |
| 684 | + interval=1, |
| 685 | +) |
| 686 | +def trigger_roboql_batch_ingest( |
| 687 | + workspace: str, |
| 688 | + batch_id: str, |
| 689 | + query: str, |
| 690 | + api_key: str, |
| 691 | + ingest_id: Optional[str] = None, |
| 692 | + batch_name: Optional[str] = None, |
| 693 | + notifications_url: Optional[str] = None, |
| 694 | +) -> None: |
| 695 | + params = {} |
| 696 | + if api_key is not None: |
| 697 | + params["api_key"] = api_key |
| 698 | + payload: Dict[str, Union[str, None]] = {"query": query} |
| 699 | + if batch_name is not None: |
| 700 | + payload["displayName"] = batch_name |
| 701 | + if ingest_id is not None: |
| 702 | + payload["ingestId"] = ingest_id |
| 703 | + if notifications_url is not None: |
| 704 | + payload["notificationsURL"] = notifications_url |
| 705 | + try: |
| 706 | + response = requests.post( |
| 707 | + f"{API_BASE_URL}/data-staging/v1/external/{workspace}/batches/{batch_id}/roboql-ingest", |
| 708 | + params=params, |
| 709 | + timeout=REQUEST_TIMEOUT, |
| 710 | + json=payload, |
| 711 | + ) |
| 712 | + except (ConnectionError, requests.exceptions.ConnectionError) as error: |
| 713 | + raise RetryError( |
| 714 | + f"Connectivity error. Try reaching Roboflow API in browser: {API_BASE_URL}" |
| 715 | + ) from error |
| 716 | + except Timeout as e: |
| 717 | + raise RetryError( |
| 718 | + f"Timeout error. Could not complete `trigger_roboql_batch_ingest(...)` operation " |
| 719 | + f"within {REQUEST_TIMEOUT} seconds. Verify connectivity and try again. " |
| 720 | + f"You may also set `ROBOFLOW_API_REQUEST_TIMEOUT` env variable to a larger " |
| 721 | + f"value (in seconds), e.g. `export ROBOFLOW_API_REQUEST_TIMEOUT=120`. " |
| 722 | + f"If the problem persists - contact Roboflow support." |
| 723 | + ) from e |
| 724 | + handle_response_errors(response=response, operation_name="trigger RoboQL ingest") |
| 725 | + |
| 726 | + |
648 | 727 | def create_videos_batch_from_directory( |
649 | 728 | directory: str, |
650 | 729 | batch_id: str, |
@@ -1567,21 +1646,21 @@ def pull_batch_element_to_directory( |
1567 | 1646 | override_existing=override_existing, |
1568 | 1647 | ): |
1569 | 1648 | return None |
1570 | | - parsed_url = urlparse(file_metadata.download_url) |
1571 | | - file_name = os.path.basename(parsed_url.path) |
1572 | | - is_archive = file_name.endswith(".tar.gz") or file_name.endswith(".tar") |
| 1649 | + is_archive = file_metadata.file_name.endswith( |
| 1650 | + ".tar.gz" |
| 1651 | + ) or file_metadata.file_name.endswith(".tar") |
1573 | 1652 | if not is_archive: |
1574 | 1653 | result_path = pull_file_to_directory( |
1575 | 1654 | url=file_metadata.download_url, |
1576 | 1655 | target_directory=target_directory, |
1577 | | - file_name=file_name, |
| 1656 | + file_name=file_metadata.file_name, |
1578 | 1657 | ) |
1579 | 1658 | export_log.denote_export(file_metadata=file_metadata, local_path=result_path) |
1580 | 1659 | return None |
1581 | 1660 | with tempfile.TemporaryDirectory() as tmp_dir: |
1582 | 1661 | download_and_unpack_archive( |
1583 | 1662 | url=file_metadata.download_url, |
1584 | | - file_name=file_name, |
| 1663 | + file_name=file_metadata.file_name, |
1585 | 1664 | pull_dir=tmp_dir, |
1586 | 1665 | target_dir=target_directory, |
1587 | 1666 | ) |
|
0 commit comments