Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refacto repo archi #1392

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/sdk/issue.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Issue module

## Queries
::: kili.presentation.client.issue.IssueClientMethods
::: kili.entrypoints.queries.issue.__init__.QueriesIssue

## Mutations
theodu marked this conversation as resolved.
Show resolved Hide resolved
::: kili.entrypoints.mutations.issue.__init__.MutationsIssue
9 changes: 7 additions & 2 deletions src/kili/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
from kili.entrypoints.queries.user import QueriesUser
from kili.entrypoints.subscriptions.label import SubscriptionsLabel
from kili.exceptions import AuthenticationFailed, UserNotFoundError
from kili.internal import KiliInternal
from kili.gateways.kili_api_gateway import KiliAPIGateway
from kili.presentation.client.internal import InternalClientMethods
from kili.presentation.client.issue import IssueClientMethods

warnings.filterwarnings("default", module="kili", category=DeprecationWarning)

Expand Down Expand Up @@ -76,6 +78,7 @@ class Kili( # pylint: disable=too-many-ancestors,too-many-instance-attributes
QueriesProjectVersion,
QueriesUser,
SubscriptionsLabel,
IssueClientMethods,
):
"""Kili Client."""

Expand Down Expand Up @@ -169,11 +172,13 @@ def __init__(
**(graphql_client_params or {}), # type: ignore
)

self.kili_api_gateway = KiliAPIGateway(self.graphql_client, self.http_client)

if not skip_checks:
api_key_query = APIKeyQuery(self.graphql_client, self.http_client)
self._check_expiry_of_key_is_close(api_key_query, self.api_key)

self.internal = KiliInternal(self)
self.internal = InternalClientMethods(self)

def _check_api_key_valid(self) -> bool:
"""Check that the api_key provided is valid."""
Expand Down
Empty file added src/kili/domain/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions src/kili/domain/issue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Issue domain."""
from dataclasses import dataclass
from typing import Literal

IssueType = Literal["ISSUE", "QUESTION"]
IssueStatus = Literal["OPEN", "SOLVED"]


@dataclass
class Issue:
"""Issue Entity."""

id_: str
50 changes: 2 additions & 48 deletions src/kili/entrypoints/mutations/issue/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Issue mutations."""

from itertools import repeat
from typing import Dict, List, Literal, Optional

from typeguard import typechecked
Expand All @@ -10,11 +9,11 @@
from kili.core.helpers import deprecate
from kili.entrypoints.base import BaseOperationEntrypointMixin
from kili.entrypoints.mutations.asset.helpers import get_asset_ids_or_throw_error
from kili.gateways.kili_api_gateway.issue.operations import GQL_CREATE_ISSUES
theodu marked this conversation as resolved.
Show resolved Hide resolved
from kili.services.helpers import assert_all_arrays_have_same_size
from kili.utils.logcontext import for_all_methods, log_call

from .helpers import get_issue_numbers, get_labels_asset_ids_map
from .queries import GQL_CREATE_ISSUES
from .helpers import get_issue_numbers


@for_all_methods(log_call, exclude=["__init__"])
Expand Down Expand Up @@ -91,51 +90,6 @@ def append_to_issues(
result = self.graphql_client.execute(GQL_CREATE_ISSUES, variables)
return self.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, project_id, "ISSUE", len(label_id_array))
label_asset_ids_map = get_labels_asset_ids_map(self, 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.graphql_client.execute(GQL_CREATE_ISSUES, variables)
return self.format_result("data", result)

@typechecked
def create_questions(
self,
Expand Down
5 changes: 0 additions & 5 deletions src/kili/entrypoints/mutations/issue/fragments.py

This file was deleted.

31 changes: 0 additions & 31 deletions src/kili/entrypoints/mutations/issue/queries.py

This file was deleted.

Empty file added src/kili/gateways/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions src/kili/gateways/kili_api_gateway/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""GraphQL gateway module."""
from kili.gateways.kili_api_gateway.issue import IssueOperationMixin


class KiliAPIGateway(IssueOperationMixin):
"""GraphQL gateway to communicate with Kili backend."""

def __init__(self, graphql_client, http_client):
theodu marked this conversation as resolved.
Show resolved Hide resolved
self.graphql_client = graphql_client
self.http_client = http_client
88 changes: 88 additions & 0 deletions src/kili/gateways/kili_api_gateway/issue/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""GraphQL Mixin extending GraphQL Gateway class with Issue related operations."""

from dataclasses import dataclass
from typing import List, Optional

from kili.core.enums import IssueStatus
from kili.core.graphql.graphql_client import GraphQLClient
from kili.core.utils.pagination import BatchIteratorBuilder
from kili.domain.issue import Issue, IssueType
from kili.gateways.kili_api_gateway.issue.operations import (
GQL_COUNT_ISSUES,
GQL_CREATE_ISSUES,
)
from kili.gateways.kili_api_gateway.issue.types import IssueToCreateKiliAPIGatewayInput
from kili.utils import tqdm


@dataclass
class IssueWhere:
"""Tuple to be passed to the IssueQuery to restrict query."""

project_id: str
asset_id: Optional[str] = None
asset_id_in: Optional[List[str]] = None
issue_type: Optional[IssueType] = None
status: Optional[IssueStatus] = None

def get_graphql_input(self):
"""Build the GraphQL IssueWhere payload to be sent in an operation."""
return {
"project": {"id": self.project_id},
"asset": {"id": self.asset_id},
"assetIn": self.asset_id_in,
"status": self.status,
"type": self.issue_type,
}


class IssueOperationMixin:
"""GraphQL Mixin extending GraphQL Gateway class with Issue related operations."""

graphql_client: GraphQLClient

def create_issues(
self, type_: IssueType, issues: List[IssueToCreateKiliAPIGatewayInput]
) -> List[Issue]:
"""Send a GraphQL request calling createIssues resolver."""
created_issue_entities: List[Issue] = []
with tqdm.tqdm(total=len(issues)) as pbar:
for issues_batch in BatchIteratorBuilder(issues):
batch_targeted_asset_ids = [issue.asset_id for issue in issues_batch]
payload = {
"issues": [
{
"issueNumber": issue.issue_number,
"labelID": issue.label_id,
"objectMid": issue.object_mid,
"type": type_,
"assetId": issue.asset_id,
"text": issue.text,
}
for issue in issues_batch
],
"where": {"idIn": batch_targeted_asset_ids},
}
result = self.graphql_client.execute(GQL_CREATE_ISSUES, payload)
batch_created_issues = result["data"]
created_issue_entities.extend(
[Issue(id_=issue["id"]) for issue in batch_created_issues]
)
pbar.update(len(issues_batch))
return created_issue_entities

def count_issues( # pylint: disable=too-many-arguments,
self,
project_id: str,
asset_id: Optional[str] = None,
asset_id_in: Optional[List[str]] = None,
issue_type: Optional[IssueType] = None,
status: Optional[IssueStatus] = None,
):
theodu marked this conversation as resolved.
Show resolved Hide resolved
"""Send a GraphQL request calling countIssues resolver."""
where = IssueWhere(project_id, asset_id, asset_id_in, issue_type, status)
payload = {
"where": where.get_graphql_input(),
}
count_result = self.graphql_client.execute(GQL_COUNT_ISSUES, payload)
return count_result["data"]
33 changes: 33 additions & 0 deletions src/kili/gateways/kili_api_gateway/issue/operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""GraphQL Issues operations."""

GQL_CREATE_ISSUES = """
mutation createIssues(
$issues: [IssueToCreate!]!
$where: AssetWhere!
) {
data: createIssues(
issues: $issues
where: $where
) {
id
}
}
"""


def get_gql_issues_query(fragment):
"""Return the GraphQL issues query."""
return f"""
query issues($where: IssueWhere!, $first: PageSize!, $skip: Int!) {{
data: issues(where: $where, first: $first, skip: $skip) {{
{fragment}
}}
}}
"""
Jonas1312 marked this conversation as resolved.
Show resolved Hide resolved


GQL_COUNT_ISSUES = """
query countIssues($where: IssueWhere!) {
data: countIssues(where: $where)
}
"""
14 changes: 14 additions & 0 deletions src/kili/gateways/kili_api_gateway/issue/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Types for the Issue-related graphql gateway functions."""
from dataclasses import dataclass
from typing import Optional


@dataclass
class IssueToCreateKiliAPIGatewayInput:
"""Data about an issue to create needed in graphql createIssue resolver."""

issue_number: int
label_id: Optional[str]
object_mid: Optional[str]
asset_id: str
text: Optional[str]
Empty file.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from kili.entrypoints.queries.api_key import QueriesApiKey


class KiliInternal(MutationsOrganization, QueriesApiKey):
class InternalClientMethods(MutationsOrganization, QueriesApiKey):
"""Inherit classes for internal use by Kili Technology only."""

def __init__(self, kili):
Expand Down
53 changes: 53 additions & 0 deletions src/kili/presentation/client/issue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Client entrypoints methods for issues."""
theodu marked this conversation as resolved.
Show resolved Hide resolved

from itertools import repeat
from typing import Dict, List, Literal, Optional

import requests
from typeguard import typechecked

from kili.gateways.kili_api_gateway import KiliAPIGateway
from kili.services.helpers import assert_all_arrays_have_same_size
from kili.use_cases.issue import IssueUseCases
from kili.use_cases.issue.types import IssueToCreateUseCaseInput
from kili.utils.logcontext import for_all_methods, log_call


@for_all_methods(log_call, exclude=["__init__"])
class IssueClientMethods:
"""Set of Issue mutations."""
Jonas1312 marked this conversation as resolved.
Show resolved Hide resolved

kili_api_gateway: KiliAPIGateway
http_client: requests.Session

@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[Literal["id"], str]]:
"""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])
issues = [
IssueToCreateUseCaseInput(label_id=label_id, object_mid=object_mid, text=text)
for (label_id, object_mid, text) in zip(
label_id_array,
object_mid_array or repeat(None),
text_array or repeat(None),
)
]
issue_service = IssueUseCases(self.kili_api_gateway)
issues_entities = issue_service.create_issues(project_id=project_id, issues=issues)
return [{"id": issue.id_} for issue in issues_entities]
Empty file added src/kili/use_cases/__init__.py
Empty file.
Loading