def import_annotations(
    sample_collection,
    project_name=None,
    project_id=None,
    task_ids=None,
    data_path=None,
    label_types=None,
    insert_new=True,
    download_media=False,
    num_workers=None,
    occluded_attr=None,
    group_id_attr=None,
    backend="cvat",
    **kwargs,
):
    """Imports annotations from the specified CVAT project or task(s) into the
    given sample collection.

    Provide one of ``project_name``, ``project_id``, or ``task_ids`` to perform
    an import.

    This method can be configured in any of the following three ways:

    1.  Pass the ``data_path`` argument to define a mapping between media
        filenames in CVAT and local filepaths to the same media.

    2.  Pass the ``download_media=True`` option to download both the
        annotations and the media files themselves, which are stored in a
        directory you specify via the ``data_path`` argument.

    3.  Don't provide ``data_path`` or ``download_media=True``, in which case
        it is assumed that the CVAT filenames correspond to the base filenames
        of existing sample filepaths in the provided ``sample_collection``.

    Args:
        sample_collection: a
            :class:`fiftyone.core.collections.SampleCollection`
        project_name (None): the name of a CVAT project to import
        project_id (None): the ID of a CVAT project to import
        task_ids (None): a CVAT task ID or iterable of CVAT task IDs to import
        data_path (None): a parameter that defines the correspondence between
            the filenames in CVAT and the filepaths of ``sample_collection``.
            Can be any of the following:

            -   a directory on disk where the media files reside. In this case,
                the filenames must match those in CVAT
            -   a dict mapping CVAT filenames to absolute filepaths to the
                corresponding media on disk
            -   the path to a JSON manifest on disk containing a mapping
                between CVAT filenames and absolute filepaths to the media on
                disk

            By default, only annotations whose filename matches an existing
            filepath in ``sample_collection`` will be imported
        label_types (None): an optional parameter specifying the label types to
            import. Can be any of the following:

            -   ``None`` (default): all label types will be stored in fields of
                the same name on ``sample_collection``
            -   a list of label types to load. In this case, the labels will be
                stored in fields of the same names in ``sample_collection``
            -   a dict mapping label types to field names of
                ``sample_collection`` in which to store the labels
            -   ``"prompt"``: present an interactive prompt to decide/discard
                field names in which to store each label type
        insert_new (True): whether to create new samples for any media for
            which annotations are found in CVAT but which do not exist in
            ``sample_collection``
        download_media (False): whether to download the images or videos found
            in CVAT to the directory or filepaths in ``data_path`` if not
            already present
        num_workers (None): a suggested number of threads to use when
            downloading media
        occluded_attr (None): an optional attribute name in which to store the
            occlusion information for all spatial labels
        group_id_attr (None): an optional attribute name in which to store the
            group id for labels
        backend ("cvat"): the name of the CVAT backend to use
        **kwargs: CVAT authentication credentials to pass to
            :class:`CVATBackendConfig`
    """
    if sample_collection.media_type == fom.GROUP:
        if insert_new:
            raise ValueError(
                "insert_new=True is not supported for grouped collections"
            )

        sample_collection = sample_collection.select_group_slices(
            _allow_mixed=True
        )

    if bool(project_name) + bool(project_id) + bool(task_ids) != 1:
        raise ValueError(
            "Exactly one of 'project_name', 'project_id', or 'task_ids' must "
            "be provided"
        )

    config = foua._parse_config(
        backend,
        None,
        occluded_attr=occluded_attr,
        group_id_attr=group_id_attr,
        **kwargs,
    )
    anno_backend = config.build()
    api = anno_backend.connect_to_api()

    if project_name is not None:
        project_id = api.get_project_id(project_name)

    if project_id is not None:
        task_ids = api.get_project_tasks(project_id)

    if etau.is_str(task_ids):
        task_ids = [task_ids]
    else:
        task_ids = list(task_ids)

    # Build mapping from CVAT filenames to local filepaths
    data_dir = None
    existing_filepaths = sample_collection.values("filepath")
    if data_path is None:
        data_map = {os.path.basename(f): f for f in existing_filepaths}
    elif etau.is_str(data_path) and data_path.endswith(".json"):
        data_map = etas.read_json(data_path)
    elif etau.is_str(data_path):
        if os.path.isdir(data_path):
            data_map = {
                os.path.basename(f): f
                for f in etau.list_files(
                    data_path, abs_paths=True, recursive=True
                )
            }
        else:
            data_map = {}

        data_dir = data_path
    else:
        data_map = data_path

    # Determine what filepaths we have annotations for
    cvat_id_map = {}
    task_filepaths = []
    ignored_filenames = []
    download_tasks = []
    for task_id in task_ids:
        cvat_id_map[task_id] = _parse_task_metadata(
            api,
            task_id,
            data_map,
            task_filepaths,
            ignored_filenames,
            download_tasks,
            data_dir=data_dir,
            download_media=download_media,
        )

    # Download media from CVAT, if requested
    if download_tasks:
        _download_media(download_tasks, num_workers)

    if ignored_filenames:
        logger.warning(
            "Ignoring annotations for %d files in CVAT (eg %s) that do not "
            "appear in the provided data map",
            len(ignored_filenames),
            ignored_filenames[0],
        )

    if not task_filepaths:
        logger.warning("No applicable annotations found to download")
        return

    dataset = sample_collection._dataset

    new_filepaths = set(task_filepaths) - set(existing_filepaths)

    # Insert samples for new filepaths, if necessary and we're allowed to
    if new_filepaths:
        if insert_new:
            dataset.add_samples([Sample(filepath=fp) for fp in new_filepaths])
        else:
            logger.warning(
                "Ignoring annotations for %d filepaths (eg %s) that do not "
                "appear in the input collection",
                len(new_filepaths),
                next(iter(new_filepaths)),
            )

    if dataset.media_type == fom.VIDEO:
        # The download implementation requires IDs for all possible frames
        dataset.select_by("filepath", task_filepaths).ensure_frames()

    anno_key = "tmp_" + str(ObjectId())
    anno_backend.register_run(dataset, anno_key, overwrite=False)

    # Download annotations
    try:
        if project_id is not None:
            # CVAT projects share a label schema, so we can download all tasks
            # in one batch
            label_schema = api._get_label_schema(
                project_id=project_id,
                occluded_attr=occluded_attr,
                group_id_attr=group_id_attr,
            )

            _download_annotations(
                dataset,
                task_ids,
                cvat_id_map,
                label_schema,
                label_types,
                anno_backend,
                anno_key,
                **kwargs,
            )
        else:
            # Each task may have a different label schema, so we must download
            # each task separately
            for task_id in task_ids:
                label_schema = api._get_label_schema(
                    task_id=task_id,
                    occluded_attr=occluded_attr,
                    group_id_attr=group_id_attr,
                )

                _download_annotations(
                    dataset,
                    [task_id],
                    cvat_id_map,
                    label_schema,
                    label_types,
                    anno_backend,
                    anno_key,
                    **kwargs,
                )
    finally:
        anno_backend.delete_run(dataset, anno_key)
        api.close()
Imports annotations from the specified CVAT project or task(s) into the
    given sample collection.

    Provide one of ``project_name``, ``project_id``, or ``task_ids`` to perform
    an import.

    This method can be configured in any of the following three ways:

    1.  Pass the ``data_path`` argument to define a mapping between media
        filenames in CVAT and local filepaths to the same media.

    2.  Pass the ``download_media=True`` option to download both the
        annotations and the media files themselves, which are stored in a
        directory you specify via the ``data_path`` argument.

    3.  Don't provide ``data_path`` or ``download_media=True``, in which case
        it is assumed that the CVAT filenames correspond to the base filenames
        of existing sample filepaths in the provided ``sample_collection``.

    Args:
        sample_collection: a
            :class:`fiftyone.core.collections.SampleCollection`
        project_name (None): the name of a CVAT project to import
        project_id (None): the ID of a CVAT project to import
        task_ids (None): a CVAT task ID or iterable of CVAT task IDs to import
        data_path (None): a parameter that defines the correspondence between
            the filenames in CVAT and the filepaths of ``sample_collection``.
            Can be any of the following:

            -   a directory on disk where the media files reside. In this case,
                the filenames must match those in CVAT
            -   a dict mapping CVAT filenames to absolute filepaths to the
                corresponding media on disk
            -   the path to a JSON manifest on disk containing a mapping
                between CVAT filenames and absolute filepaths to the media on
                disk

            By default, only annotations whose filename matches an existing
            filepath in ``sample_collection`` will be imported
        label_types (None): an optional parameter specifying the label types to
            import. Can be any of the following:

            -   ``None`` (default): all label types will be stored in fields of
                the same name on ``sample_collection``
            -   a list of label types to load. In this case, the labels will be
                stored in fields of the same names in ``sample_collection``
            -   a dict mapping label types to field names of
                ``sample_collection`` in which to store the labels
            -   ``"prompt"``: present an interactive prompt to decide/discard
                field names in which to store each label type
        insert_new (True): whether to create new samples for any media for
            which annotations are found in CVAT but which do not exist in
            ``sample_collection``
        download_media (False): whether to download the images or videos found
            in CVAT to the directory or filepaths in ``data_path`` if not
            already present
        num_workers (None): a suggested number of threads to use when
            downloading media
        occluded_attr (None): an optional attribute name in which to store the
            occlusion information for all spatial labels
        group_id_attr (None): an optional attribute name in which to store the
            group id for labels
        backend ("cvat"): the name of the CVAT backend to use
        **kwargs: CVAT authentication credentials to pass to
            :class:`CVATBackendConfig`
    
fiftyone/utils/cvat.py