Skip to content

Issue module

Queries

Set of Issue queries.

Source code in kili/entrypoints/queries/issue/__init__.py
class QueriesIssue:
    """Set of Issue queries."""

    # pylint: disable=too-many-arguments,dangerous-default-value

    def __init__(self, auth: KiliAuth):
        """Initialize the subclass.

        Args:
            auth: KiliAuth object
        """
        self.auth = auth

    @overload
    def issues(
        self,
        project_id: str,
        fields: List[str] = [
            "id",
            "createdAt",
            "issueNumber",
            "status",
            "type",
        ],
        first: Optional[int] = None,
        skip: int = 0,
        disable_tqdm: bool = False,
        asset_id: Optional[str] = None,
        asset_id_in: Optional[List[str]] = None,
        issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
        status: Optional[Literal["OPEN", "SOLVED"]] = None,
        *,
        as_generator: Literal[True],
    ) -> Generator[Dict, None, None]:
        ...

    @overload
    def issues(
        self,
        project_id: str,
        fields: List[str] = [
            "id",
            "createdAt",
            "issueNumber",
            "status",
            "type",
        ],
        first: Optional[int] = None,
        skip: int = 0,
        disable_tqdm: bool = False,
        asset_id: Optional[str] = None,
        asset_id_in: Optional[List[str]] = None,
        issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
        status: Optional[Literal["OPEN", "SOLVED"]] = None,
        *,
        as_generator: Literal[False] = False,
    ) -> List[Dict]:
        ...

    @typechecked
    def issues(
        self,
        project_id: str,
        fields: List[str] = [
            "id",
            "createdAt",
            "issueNumber",
            "status",
            "type",
            "assetId",
        ],
        first: Optional[int] = None,
        skip: int = 0,
        disable_tqdm: bool = False,
        asset_id: Optional[str] = None,
        asset_id_in: Optional[List[str]] = None,
        issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
        status: Optional[Literal["OPEN", "SOLVED"]] = None,
        *,
        as_generator: bool = False,
    ) -> Iterable[Dict]:
        # pylint: disable=line-too-long
        """Get a generator or a list of issues that match a set of criteria.

        !!! Info "Issues or Questions"
            An `Issue` object both represent an issue and a question in the app.
            To create them, two different methods are provided: `create_issues` and `create_questions`.
            However to query issues and questions, we currently provide this unique method that retrieves both of them.

        Args:
            project_id: Project ID the issue belongs to.
            asset_id: Id of the asset whose returned issues are associated to.
            asset_id_in: List of Ids of assets whose returned issues are associated to.
            issue_type: Type of the issue to return. An issue object both represents issues and questions in the app.
            status: Status of the issues to return.
            fields: All the fields to request among the possible fields for the assets.
                See [the documentation](https://docs.kili-technology.com/reference/graphql-api#issue) for all possible fields.
            first: Maximum number of issues to return.
            skip: Number of issues to skip (they are ordered by their date of creation, first to last).
            disable_tqdm: If `True`, the progress bar will be disabled
            as_generator: If `True`, a generator on the issues is returned.
        Returns:
            A result object which contains the query if it was successful,
                or an error message.
        Examples:
            >>> kili.issues(project_id=project_id, fields=['author.email']) # List all issues of a project and their authors
        """

        if asset_id and asset_id_in:
            raise ValueError(
                "You cannot provide both `asset_id` and `asset_id_in` at the same time"
            )
        where = IssueWhere(
            project_id=project_id,
            asset_id=asset_id,
            asset_id_in=asset_id_in,
            issue_type=issue_type,
            status=status,
        )
        disable_tqdm = disable_tqdm_if_as_generator(as_generator, disable_tqdm)
        options = QueryOptions(disable_tqdm, first, skip)
        issues_gen = IssueQuery(self.auth.client)(where, fields, options)
        if as_generator:
            return issues_gen
        return list(issues_gen)

    @typechecked
    def count_issues(
        self,
        project_id: str,
        asset_id: Optional[str] = None,
        asset_id_in: Optional[List[str]] = None,
        issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
        status: Optional[Literal["OPEN", "SOLVED"]] = None,
    ) -> int:
        """Count and return the number of issues with the given constraints.

        Args:
            project_id: Project ID the issue belongs to.
            asset_id: Asset id whose returned issues are associated to.
            asset_id_in: List of asset ids whose returned issues are associated to.
            issue_type: Type of the issue to return. An issue object both
                represents issues and questions in the app
            status: Status of the issues to return.
        Returns:
            The number of issues with the parameters provided
        """
        if asset_id and asset_id_in:
            raise ValueError(
                "You cannot provide both `asset_id` and `asset_id_in` at the same time"
            )
        where = IssueWhere(
            project_id=project_id,
            asset_id=asset_id,
            asset_id_in=asset_id_in,
            issue_type=issue_type,
            status=status,
        )
        return IssueQuery(self.auth.client).count(where)

count_issues(self, project_id, asset_id=None, asset_id_in=None, issue_type=None, status=None)

Count and return the number of issues with the given constraints.

Parameters:

Name Type Description Default
project_id str

Project ID the issue belongs to.

required
asset_id Optional[str]

Asset id whose returned issues are associated to.

None
asset_id_in Optional[List[str]]

List of asset ids whose returned issues are associated to.

None
issue_type Optional[typing_extensions.Literal['QUESTION', 'ISSUE']]

Type of the issue to return. An issue object both represents issues and questions in the app

None
status Optional[typing_extensions.Literal['OPEN', 'SOLVED']]

Status of the issues to return.

None

Returns:

Type Description
int

The number of issues with the parameters provided

Source code in kili/entrypoints/queries/issue/__init__.py
@typechecked
def count_issues(
    self,
    project_id: str,
    asset_id: Optional[str] = None,
    asset_id_in: Optional[List[str]] = None,
    issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
    status: Optional[Literal["OPEN", "SOLVED"]] = None,
) -> int:
    """Count and return the number of issues with the given constraints.

    Args:
        project_id: Project ID the issue belongs to.
        asset_id: Asset id whose returned issues are associated to.
        asset_id_in: List of asset ids whose returned issues are associated to.
        issue_type: Type of the issue to return. An issue object both
            represents issues and questions in the app
        status: Status of the issues to return.
    Returns:
        The number of issues with the parameters provided
    """
    if asset_id and asset_id_in:
        raise ValueError(
            "You cannot provide both `asset_id` and `asset_id_in` at the same time"
        )
    where = IssueWhere(
        project_id=project_id,
        asset_id=asset_id,
        asset_id_in=asset_id_in,
        issue_type=issue_type,
        status=status,
    )
    return IssueQuery(self.auth.client).count(where)

issues(self, project_id, fields=['id', 'createdAt', 'issueNumber', 'status', 'type', 'assetId'], first=None, skip=0, disable_tqdm=False, asset_id=None, asset_id_in=None, issue_type=None, status=None, *, as_generator=False)

Get a generator or a list of issues that match a set of criteria.

Issues or Questions

An Issue object both represent an issue and a question in the app. To create them, two different methods are provided: create_issues and create_questions. However to query issues and questions, we currently provide this unique method that retrieves both of them.

Parameters:

Name Type Description Default
project_id str

Project ID the issue belongs to.

required
asset_id Optional[str]

Id of the asset whose returned issues are associated to.

None
asset_id_in Optional[List[str]]

List of Ids of assets whose returned issues are associated to.

None
issue_type Optional[typing_extensions.Literal['QUESTION', 'ISSUE']]

Type of the issue to return. An issue object both represents issues and questions in the app.

None
status Optional[typing_extensions.Literal['OPEN', 'SOLVED']]

Status of the issues to return.

None
fields List[str]

All the fields to request among the possible fields for the assets. See the documentation for all possible fields.

['id', 'createdAt', 'issueNumber', 'status', 'type', 'assetId']
first Optional[int]

Maximum number of issues to return.

None
skip int

Number of issues to skip (they are ordered by their date of creation, first to last).

0
disable_tqdm bool

If True, the progress bar will be disabled

False
as_generator bool

If True, a generator on the issues is returned.

False

Returns:

Type Description
Iterable[Dict]

A result object which contains the query if it was successful, or an error message.

Examples:

>>> kili.issues(project_id=project_id, fields=['author.email']) # List all issues of a project and their authors
Source code in kili/entrypoints/queries/issue/__init__.py
@typechecked
def issues(
    self,
    project_id: str,
    fields: List[str] = [
        "id",
        "createdAt",
        "issueNumber",
        "status",
        "type",
        "assetId",
    ],
    first: Optional[int] = None,
    skip: int = 0,
    disable_tqdm: bool = False,
    asset_id: Optional[str] = None,
    asset_id_in: Optional[List[str]] = None,
    issue_type: Optional[Literal["QUESTION", "ISSUE"]] = None,
    status: Optional[Literal["OPEN", "SOLVED"]] = None,
    *,
    as_generator: bool = False,
) -> Iterable[Dict]:
    # pylint: disable=line-too-long
    """Get a generator or a list of issues that match a set of criteria.

    !!! Info "Issues or Questions"
        An `Issue` object both represent an issue and a question in the app.
        To create them, two different methods are provided: `create_issues` and `create_questions`.
        However to query issues and questions, we currently provide this unique method that retrieves both of them.

    Args:
        project_id: Project ID the issue belongs to.
        asset_id: Id of the asset whose returned issues are associated to.
        asset_id_in: List of Ids of assets whose returned issues are associated to.
        issue_type: Type of the issue to return. An issue object both represents issues and questions in the app.
        status: Status of the issues to return.
        fields: All the fields to request among the possible fields for the assets.
            See [the documentation](https://docs.kili-technology.com/reference/graphql-api#issue) for all possible fields.
        first: Maximum number of issues to return.
        skip: Number of issues to skip (they are ordered by their date of creation, first to last).
        disable_tqdm: If `True`, the progress bar will be disabled
        as_generator: If `True`, a generator on the issues is returned.
    Returns:
        A result object which contains the query if it was successful,
            or an error message.
    Examples:
        >>> kili.issues(project_id=project_id, fields=['author.email']) # List all issues of a project and their authors
    """

    if asset_id and asset_id_in:
        raise ValueError(
            "You cannot provide both `asset_id` and `asset_id_in` at the same time"
        )
    where = IssueWhere(
        project_id=project_id,
        asset_id=asset_id,
        asset_id_in=asset_id_in,
        issue_type=issue_type,
        status=status,
    )
    disable_tqdm = disable_tqdm_if_as_generator(as_generator, disable_tqdm)
    options = QueryOptions(disable_tqdm, first, skip)
    issues_gen = IssueQuery(self.auth.client)(where, fields, options)
    if as_generator:
        return issues_gen
    return list(issues_gen)

Mutations

Set of Issue mutations.

Source code in kili/entrypoints/mutations/issue/__init__.py
class MutationsIssue:
    """Set of Issue mutations."""

    # pylint: disable=too-many-arguments

    def __init__(self, auth: KiliAuth):
        """Initialize the subclass.

        Args:
            auth: KiliAuth object
        """
        self.auth = auth

    @deprecate(
        msg=(
            "append_to_issues is deprecated. Please use `create_issues` or `create_questions`"
            " instead. These new methods allow to import several issues or questions at the same"
            " time and provide better performances."
        )
    )
    @typechecked
    def append_to_issues(
        self,
        label_id: str,
        project_id: str,
        object_mid: Optional[str] = None,
        text: Optional[str] = None,
        type_: Literal["ISSUE", "QUESTION"] = "ISSUE",
    ) -> Dict:
        """Create an issue.

        !!! danger "Deprecated"
            append_to_issues is deprecated.
            Please use `create_issues` or `create_questions` instead.
            These new methods allow to import several issues or questions at the same time
            and provide better performances.

        Args:
            label_id: Id of the label to add an issue to
            object_mid: Mid of the object in the label to associate the issue to
            type_: type of the issue to add. Can be either "ISSUE" or "QUESTION"
            text: If given, write a comment related to the issue
            project_id: Id of the project

        Returns:
            A result object which indicates if the mutation was successful,
                or an error message.
        """
        issue_number = get_issue_numbers(self.auth, project_id, type_, 1)[0]
        try:
            options = QueryOptions(disable_tqdm=True)
            where = LabelWhere(
                project_id=project_id,
                label_id=label_id,
            )
            asset_id: str = list(
                LabelQuery(self.auth.client)(where=where, fields=["labelOf.id"], options=options)
            )[0]["labelOf"]["id"]
        except:
            # pylint: disable=raise-missing-from
            raise ValueError(
                f"Label ID {label_id} does not exist in the project of ID {project_id}"
            )
        variables = {
            "issues": [
                {
                    "issueNumber": issue_number,
                    "labelID": label_id,
                    "objectMid": object_mid,
                    "type": type_,
                    "assetId": asset_id,
                    "text": text,
                }
            ],
            "where": {"id": asset_id},
        }

        result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
        return format_result("data", result)[0]

    @typechecked
    def create_issues(
        self,
        project_id: str,
        label_id_array: List[str],
        object_mid_array: Optional[List[Optional[str]]] = None,
        text_array: Optional[List[Optional[str]]] = None,
    ) -> List[Dict]:
        """Create an issue.

        Args:
            project_id: Id of the project.
            label_id_array: List of Ids of the labels to add an issue to.
            object_mid_array: List of mids of the objects in the labels to associate the issues to.
            text_array: List of texts to associate to the issues.

        Returns:
            A list of dictionary with the `id` key of the created issues.
        """
        assert_all_arrays_have_same_size([label_id_array, object_mid_array, text_array])
        issue_number_array = get_issue_numbers(self.auth, project_id, "ISSUE", len(label_id_array))
        label_asset_ids_map = get_labels_asset_ids_map(self.auth, project_id, label_id_array)
        variables = {
            "issues": [
                {
                    "issueNumber": issue_number,
                    "labelID": label_id,
                    "objectMid": object_mid,
                    "type": "ISSUE",
                    "assetId": label_asset_ids_map[label_id],
                    "text": text,
                }
                for (issue_number, label_id, object_mid, text) in zip(
                    issue_number_array,
                    label_id_array,
                    object_mid_array or repeat(None),
                    text_array or repeat(None),
                )
            ],
            "where": {"idIn": list(label_asset_ids_map.values())},
        }

        result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
        return format_result("data", result)

    @typechecked
    def create_questions(
        self,
        project_id: str,
        text_array: List[Optional[str]],
        asset_id_array: Optional[List[str]] = None,
        asset_external_id_array: Optional[List[str]] = None,
    ) -> List[Dict]:
        # pylint:disable=line-too-long
        """Create questions.

        Args:
            project_id: Id of the project.
            text_array: List of question strings.
            asset_id_array: List of the assets to add the questions to.
            asset_external_id_array: List of the assets to add the questions to. Used if `asset_id_array` is not given.

        Returns:
            A list of dictionary with the `id` key of the created questions.
        """
        assert_all_arrays_have_same_size([text_array, asset_id_array])
        issue_number_array = get_issue_numbers(self.auth, project_id, "QUESTION", len(text_array))
        asset_id_array = get_asset_ids_or_throw_error(
            self.auth, asset_id_array, asset_external_id_array, project_id
        )
        variables = {
            "issues": [
                {"issueNumber": issue_number, "type": "QUESTION", "assetId": asset_id, "text": text}
                for (asset_id, text, issue_number) in zip(
                    asset_id_array, text_array, issue_number_array
                )
            ],
            "where": {"idIn": asset_id_array},
        }

        result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
        return format_result("data", result)

append_to_issues(self, label_id, project_id, object_mid=None, text=None, type_='ISSUE')

Create an issue.

Deprecated

append_to_issues is deprecated. Please use create_issues or create_questions instead. These new methods allow to import several issues or questions at the same time and provide better performances.

Parameters:

Name Type Description Default
label_id str

Id of the label to add an issue to

required
object_mid Optional[str]

Mid of the object in the label to associate the issue to

None
type_ typing_extensions.Literal['ISSUE', 'QUESTION']

type of the issue to add. Can be either "ISSUE" or "QUESTION"

'ISSUE'
text Optional[str]

If given, write a comment related to the issue

None
project_id str

Id of the project

required

Returns:

Type Description
Dict

A result object which indicates if the mutation was successful, or an error message.

Source code in kili/entrypoints/mutations/issue/__init__.py
@deprecate(
    msg=(
        "append_to_issues is deprecated. Please use `create_issues` or `create_questions`"
        " instead. These new methods allow to import several issues or questions at the same"
        " time and provide better performances."
    )
)
@typechecked
def append_to_issues(
    self,
    label_id: str,
    project_id: str,
    object_mid: Optional[str] = None,
    text: Optional[str] = None,
    type_: Literal["ISSUE", "QUESTION"] = "ISSUE",
) -> Dict:
    """Create an issue.

    !!! danger "Deprecated"
        append_to_issues is deprecated.
        Please use `create_issues` or `create_questions` instead.
        These new methods allow to import several issues or questions at the same time
        and provide better performances.

    Args:
        label_id: Id of the label to add an issue to
        object_mid: Mid of the object in the label to associate the issue to
        type_: type of the issue to add. Can be either "ISSUE" or "QUESTION"
        text: If given, write a comment related to the issue
        project_id: Id of the project

    Returns:
        A result object which indicates if the mutation was successful,
            or an error message.
    """
    issue_number = get_issue_numbers(self.auth, project_id, type_, 1)[0]
    try:
        options = QueryOptions(disable_tqdm=True)
        where = LabelWhere(
            project_id=project_id,
            label_id=label_id,
        )
        asset_id: str = list(
            LabelQuery(self.auth.client)(where=where, fields=["labelOf.id"], options=options)
        )[0]["labelOf"]["id"]
    except:
        # pylint: disable=raise-missing-from
        raise ValueError(
            f"Label ID {label_id} does not exist in the project of ID {project_id}"
        )
    variables = {
        "issues": [
            {
                "issueNumber": issue_number,
                "labelID": label_id,
                "objectMid": object_mid,
                "type": type_,
                "assetId": asset_id,
                "text": text,
            }
        ],
        "where": {"id": asset_id},
    }

    result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
    return format_result("data", result)[0]

create_issues(self, project_id, label_id_array, object_mid_array=None, text_array=None)

Create an issue.

Parameters:

Name Type Description Default
project_id str

Id of the project.

required
label_id_array List[str]

List of Ids of the labels to add an issue to.

required
object_mid_array Optional[List[Union[str, NoneType]]]

List of mids of the objects in the labels to associate the issues to.

None
text_array Optional[List[Union[str, NoneType]]]

List of texts to associate to the issues.

None

Returns:

Type Description
List[Dict]

A list of dictionary with the id key of the created issues.

Source code in kili/entrypoints/mutations/issue/__init__.py
@typechecked
def create_issues(
    self,
    project_id: str,
    label_id_array: List[str],
    object_mid_array: Optional[List[Optional[str]]] = None,
    text_array: Optional[List[Optional[str]]] = None,
) -> List[Dict]:
    """Create an issue.

    Args:
        project_id: Id of the project.
        label_id_array: List of Ids of the labels to add an issue to.
        object_mid_array: List of mids of the objects in the labels to associate the issues to.
        text_array: List of texts to associate to the issues.

    Returns:
        A list of dictionary with the `id` key of the created issues.
    """
    assert_all_arrays_have_same_size([label_id_array, object_mid_array, text_array])
    issue_number_array = get_issue_numbers(self.auth, project_id, "ISSUE", len(label_id_array))
    label_asset_ids_map = get_labels_asset_ids_map(self.auth, project_id, label_id_array)
    variables = {
        "issues": [
            {
                "issueNumber": issue_number,
                "labelID": label_id,
                "objectMid": object_mid,
                "type": "ISSUE",
                "assetId": label_asset_ids_map[label_id],
                "text": text,
            }
            for (issue_number, label_id, object_mid, text) in zip(
                issue_number_array,
                label_id_array,
                object_mid_array or repeat(None),
                text_array or repeat(None),
            )
        ],
        "where": {"idIn": list(label_asset_ids_map.values())},
    }

    result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
    return format_result("data", result)

create_questions(self, project_id, text_array, asset_id_array=None, asset_external_id_array=None)

Create questions.

Parameters:

Name Type Description Default
project_id str

Id of the project.

required
text_array List[Optional[str]]

List of question strings.

required
asset_id_array Optional[List[str]]

List of the assets to add the questions to.

None
asset_external_id_array Optional[List[str]]

List of the assets to add the questions to. Used if asset_id_array is not given.

None

Returns:

Type Description
List[Dict]

A list of dictionary with the id key of the created questions.

Source code in kili/entrypoints/mutations/issue/__init__.py
@typechecked
def create_questions(
    self,
    project_id: str,
    text_array: List[Optional[str]],
    asset_id_array: Optional[List[str]] = None,
    asset_external_id_array: Optional[List[str]] = None,
) -> List[Dict]:
    # pylint:disable=line-too-long
    """Create questions.

    Args:
        project_id: Id of the project.
        text_array: List of question strings.
        asset_id_array: List of the assets to add the questions to.
        asset_external_id_array: List of the assets to add the questions to. Used if `asset_id_array` is not given.

    Returns:
        A list of dictionary with the `id` key of the created questions.
    """
    assert_all_arrays_have_same_size([text_array, asset_id_array])
    issue_number_array = get_issue_numbers(self.auth, project_id, "QUESTION", len(text_array))
    asset_id_array = get_asset_ids_or_throw_error(
        self.auth, asset_id_array, asset_external_id_array, project_id
    )
    variables = {
        "issues": [
            {"issueNumber": issue_number, "type": "QUESTION", "assetId": asset_id, "text": text}
            for (asset_id, text, issue_number) in zip(
                asset_id_array, text_array, issue_number_array
            )
        ],
        "where": {"idIn": asset_id_array},
    }

    result = self.auth.client.execute(GQL_CREATE_ISSUES, variables)
    return format_result("data", result)