Skip to content

Commit 6552464

Browse files
authored
feat: [DEV-12997]: Bundles (#391)
1 parent 907f23d commit 6552464

6 files changed

Lines changed: 94 additions & 37 deletions

File tree

indico/client/request.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ def process_response(self, response: "AnyDict") -> "ResponseType":
7070
return cast("ResponseType", raw_response)
7171

7272

73+
def _parse_nested_response(
74+
response: "AnyDict", nested_keys: "List[str | int]"
75+
) -> "Any":
76+
composite: "Any" = response
77+
for key in nested_keys:
78+
if isinstance(composite, list):
79+
if not isinstance(key, int):
80+
raise IndicoInputError(
81+
f"Invalid nested key type: {type(key)}",
82+
)
83+
composite = composite[int(key)]
84+
continue
85+
86+
if isinstance(composite, dict):
87+
if key not in composite.keys():
88+
raise IndicoInputError(
89+
f"Nested key not found in response: {key}",
90+
)
91+
composite = composite[key]
92+
93+
return composite
94+
95+
7396
class PagedRequest(GraphQLRequest[ResponseType]):
7497
"""
7598
To enable pagination, query must include $after as an argument
@@ -100,19 +123,11 @@ def __init__(self, query: str, variables: "Optional[AnyDict]" = None):
100123
super().__init__(query, variables=variables)
101124

102125
def parse_payload(
103-
self, response: "AnyDict", nested_keys: "Optional[List[str]]" = None
126+
self, response: "AnyDict", nested_keys: "Optional[List[str | int]]" = None
104127
) -> "Any":
105128
raw_response: "AnyDict" = cast("AnyDict", super().parse_payload(response))
106-
107129
if nested_keys:
108-
composite = raw_response
109-
for key in nested_keys:
110-
if key not in composite.keys():
111-
raise IndicoInputError(
112-
f"Nested key not found in response: {key}",
113-
)
114-
composite = composite[key]
115-
130+
composite: "Any" = _parse_nested_response(raw_response, nested_keys)
116131
_pg = composite.get("pageInfo")
117132
else:
118133
_pg = next(iter(raw_response.values())).get("pageInfo")
@@ -157,19 +172,12 @@ def __init__(self, query: str, variables: "Optional[AnyDict]" = None):
157172
super().__init__(query, variables=variables)
158173

159174
def parse_payload(
160-
self, response: "AnyDict", nested_keys: "Optional[List[str]]" = None
175+
self, response: "AnyDict", nested_keys: "Optional[List[str | int]]" = None
161176
) -> "Any":
162177
raw_response: "AnyDict" = cast("AnyDict", super().parse_payload(response))
163178

164179
if nested_keys:
165-
composite = raw_response
166-
for key in nested_keys:
167-
if key not in composite.keys():
168-
raise IndicoInputError(
169-
f"Nested key not found in response: {key}",
170-
)
171-
composite = composite[key]
172-
180+
composite: "Any" = _parse_nested_response(raw_response, nested_keys)
173181
pagination_data = composite
174182
else:
175183
pagination_data = next(iter(raw_response.values()))

indico/filters/__init__.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,26 +147,50 @@ class ModelGroupExampleFilter(Filter):
147147
148148
Args:
149149
file_name (str): examples with input file names containing this string
150+
file_type (str): examples with this file type (e.g. "PDF")
151+
labeler (List[int]): examples labeled by these users
152+
include_class (List[int]): examples that are or are not included in this class
153+
exclude_class (List[int]): examples that are or are not excluded from this class
150154
partial (bool): examples that are or are not partially labeled
155+
autolabeled (bool): examples that are or are not autolabeled
151156
status (str): submissions in this status. Options:
152157
[COMPLETE, INCOMPLETE]
153158
text_search (bool): examples that contain this substring in their text
154159
Returns:
155160
dict containing query filter parameters
156161
"""
157162

158-
# TODO: extend to support full filter list
159-
__options__ = ("file_name", "partial", "status", "text_search")
163+
__options__ = (
164+
"file_name",
165+
"file_type",
166+
"labeler",
167+
"include_class",
168+
"exclude_class",
169+
"partial",
170+
"autolabeled",
171+
"status",
172+
"text_search",
173+
)
160174

161175
def __init__(
162176
self,
163177
file_name: "Optional[str]" = None,
178+
file_type: "Optional[str]" = None,
179+
labeler: "Optional[List[int]]" = None,
180+
include_class: "Optional[List[int]]" = None,
181+
exclude_class: "Optional[List[int]]" = None,
182+
autolabeled: "Optional[bool]" = None,
164183
partial: "Optional[bool]" = None,
165184
status: "Optional[str]" = None,
166185
text_search: "Optional[str]" = None,
167186
):
168-
kwargs: "Dict[str, Optional[Union[bool, str]]]" = {
187+
kwargs: "Dict[str, Optional[Union[bool, str, List[int]]]]" = {
169188
"fileName": file_name,
189+
"fileType": file_type,
190+
"labeler": labeler,
191+
"includeClass": include_class,
192+
"excludeClass": exclude_class,
193+
"autolabeled": autolabeled,
170194
"partial": partial,
171195
"textSearch": text_search,
172196
}

indico/queries/example.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,28 @@ class ListModelGroupExamples(PagedRequest["List[Example]"]):
1818
Options:
1919
model_group_ids (List[int]): Model group ids to filter by
2020
filters (ModelGroupExampleFilter or Dict): Example attributes to filter by
21-
limit (int, default=1000): Maximum number of Examples to return
21+
limit (int, default=1000): Maximum number of Examples to return per page
2222
orderBy (str, default="ID"): Example attribute to filter by
2323
desc: (bool, default=True): List in descending order
24+
after (int): id to start the pagination from
25+
before (int): id to end the pagination at
2426
2527
Returns:
2628
List[Example]: All the found Example objects
2729
If paginated, yields results one at a time
2830
"""
2931

3032
query = """
31-
query GetExamples($modelGroupId:Int, $orderBy: ExampleOrder, $desc: Boolean, $limit: Int, $filters: ModelGroupExampleFilter, $after: Int, $before: Int) {
33+
query GetExamples($modelGroupId:Int, $orderBy: ExampleOrder, $desc: Boolean, $limit: Int, $filters: ExampleFilter, $after: Int, $before: Int) {
3234
modelGroups(modelGroupIds: [$modelGroupId]) {
3335
modelGroups {
34-
pagedExamples(orderBy:$orderBy, desc:$desc, limit: $limit, filters: $filters, after: $after, before: $before) {
36+
pagedExamples(orderBy: $orderBy, desc: $desc, limit: $limit, filters: $filters, after: $after, before: $before) {
3537
examples {
3638
id
3739
status
38-
datafileId
40+
datafileIds
41+
originalDatafileId
42+
originalDatafileName
3943
}
4044
pageInfo {
4145
startCursor
@@ -74,5 +78,12 @@ def __init__(
7478
)
7579

7680
def process_response(self, response: "Payload") -> "List[Example]":
77-
example_page = super().parse_payload(response)["modelGroups"]["modelGroups"][0]
78-
return [Example(**s) for s in example_page["pagedExamples"]["examples"]]
81+
example_page = super().parse_payload(
82+
response, nested_keys=["modelGroups", "modelGroups", 0, "pagedExamples"]
83+
)
84+
return [
85+
Example(**example)
86+
for example in example_page["modelGroups"]["modelGroups"][0][
87+
"pagedExamples"
88+
]["examples"]
89+
]

indico/queries/questionnaire.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,15 @@ class GetQuestionnaireExamples(GraphQLRequest["List[Example]"]):
7575
questionnaires(questionnaireIds: [$questionnaire_id]) {
7676
questionnaires {
7777
examples(numExamples: $num_examples, datafileId: $datafile_id) {
78-
rowIndex
79-
datafileId
80-
source
78+
datafileIds
79+
originalDatafileId
80+
originalDatafileName
81+
contexts {
82+
id
83+
datafileId
84+
source
85+
}
86+
status
8187
id
8288
}
8389
}

indico/types/questionnaire.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,18 @@ class Questionnaire(BaseType):
2626
question: Question
2727

2828

29-
class Example(BaseType):
30-
row_index: int
29+
class ExampleContext(BaseType):
30+
id: int
3131
datafile_id: int
32-
status: str
3332
source: str
33+
34+
35+
class Example(BaseType):
36+
datafile_ids: List[int]
37+
original_datafile_id: int
38+
original_datafile_name: str
39+
contexts: List[ExampleContext]
40+
status: str
3441
id: int
3542

3643

tests/integration/queries/test_questionnaire.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,10 @@ def test_get_examples(indico, unlabeled_questionnaire):
139139
assert len(examples) == 3
140140
for example in examples:
141141
assert isinstance(example, Example)
142-
assert isinstance(example.source, str)
143-
assert isinstance(example.row_index, int)
144-
assert isinstance(example.datafile_id, int)
142+
assert isinstance(example.contexts, list)
143+
assert isinstance(example.datafile_ids, list)
144+
assert isinstance(example.original_datafile_id, int)
145+
assert isinstance(example.original_datafile_name, str)
145146

146147

147148
def test_add_labels(indico, unlabeled_questionnaire):

0 commit comments

Comments
 (0)