Skip to content

Commit caf37e3

Browse files
committed
update PanKB interop API schema
1 parent 7f553c5 commit caf37e3

1 file changed

Lines changed: 128 additions & 36 deletions

File tree

interop_query/views.py

Lines changed: 128 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
summary="List all genes (paginated)",
1919
description=(
2020
"Return distinct (gene, species) pairs with PanKB URLs. "
21-
"Supports cursor-based pagination via skip/limit query parameters."
21+
"Supports cursor-based pagination via after/limit query parameters."
2222
),
2323
parameters=[
24-
OpenApiParameter(name="skip", type=int, location="query", description="Number of records to skip (default 0)"),
25-
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 10000, max 50000)"),
24+
OpenApiParameter(name="after", type=str, location="query", description="Cursor from previous page (next_cursor value)"),
25+
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 50000, max 200000)"),
2626
],
2727
responses={
2828
200: {
29+
"description": "Paginated list of genes with PanKB URLs.",
2930
"type": "object",
3031
"properties": {
3132
"genes": {
@@ -39,12 +40,12 @@
3940
},
4041
},
4142
},
42-
"total": {"type": "integer"},
43-
"skip": {"type": "integer"},
4443
"limit": {"type": "integer"},
44+
"next_cursor": {"type": "string", "nullable": True},
4545
"has_more": {"type": "boolean"},
4646
},
47-
}
47+
},
48+
500: {"description": "Unexpected server error."},
4849
},
4950
)
5051
@api_view(["GET"])
@@ -81,14 +82,15 @@ def genes(request):
8182
summary="List all strains (paginated)",
8283
description=(
8384
"Return all strains (genome IDs) with PanKB URLs. "
84-
"Supports cursor-based pagination via skip/limit query parameters."
85+
"Supports cursor-based pagination via after/limit query parameters."
8586
),
8687
parameters=[
87-
OpenApiParameter(name="skip", type=int, location="query", description="Number of records to skip (default 0)"),
88-
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 10000, max 50000)"),
88+
OpenApiParameter(name="after", type=str, location="query", description="Cursor from previous page (next_cursor value)"),
89+
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 50000, max 200000)"),
8990
],
9091
responses={
9192
200: {
93+
"description": "Paginated list of strains with PanKB URLs.",
9294
"type": "object",
9395
"properties": {
9496
"strains": {
@@ -101,12 +103,12 @@ def genes(request):
101103
},
102104
},
103105
},
104-
"total": {"type": "integer"},
105-
"skip": {"type": "integer"},
106106
"limit": {"type": "integer"},
107+
"next_cursor": {"type": "string", "nullable": True},
107108
"has_more": {"type": "boolean"},
108109
},
109-
}
110+
},
111+
500: {"description": "Unexpected server error."},
110112
},
111113
)
112114
@api_view(["GET"])
@@ -142,14 +144,15 @@ def strains(request):
142144
summary="List gene-strain pairs (paginated)",
143145
description=(
144146
"Return distinct (gene, strain, locus_tag) pairs with PanKB URLs. "
145-
"Supports cursor-based pagination via skip/limit query parameters."
147+
"Supports cursor-based pagination via after/limit query parameters."
146148
),
147149
parameters=[
148-
OpenApiParameter(name="skip", type=int, location="query", description="Number of records to skip (default 0)"),
149-
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 10000, max 50000)"),
150+
OpenApiParameter(name="after", type=str, location="query", description="Cursor from previous page (next_cursor value)"),
151+
OpenApiParameter(name="limit", type=int, location="query", description="Max records to return (default 50000, max 200000)"),
150152
],
151153
responses={
152154
200: {
155+
"description": "Paginated list of gene-strain pairs with PanKB URLs.",
153156
"type": "object",
154157
"properties": {
155158
"pairs": {
@@ -164,12 +167,12 @@ def strains(request):
164167
},
165168
},
166169
},
167-
"total": {"type": "integer"},
168-
"skip": {"type": "integer"},
169170
"limit": {"type": "integer"},
171+
"next_cursor": {"type": "string", "nullable": True},
170172
"has_more": {"type": "boolean"},
171173
},
172-
}
174+
},
175+
500: {"description": "Unexpected server error."},
173176
},
174177
)
175178
@api_view(["GET"])
@@ -203,7 +206,13 @@ def gene_strain_pairs(request):
203206
@extend_schema(
204207
tags=["Gene-Strain Pairs"],
205208
summary="Query by gene-strain pairs",
206-
description="Look up detailed gene info for specific gene/strain pair combinations.",
209+
description=(
210+
"Look up detailed gene info for specific gene/strain pair combinations. "
211+
"Accepts JSON body: {\"pairs\": [{\"gene\": \"...\", \"strain\": \"...\"}]}. "
212+
"Both 'gene' and 'strain' must be strings (not arrays). Each pair represents one gene-strain combination. "
213+
"Returns 400 if the pairs list is empty or entries are missing required fields. "
214+
"IDs not found in the database are silently omitted from the response."
215+
),
207216
request={
208217
"application/json": {
209218
"type": "object",
@@ -223,7 +232,30 @@ def gene_strain_pairs(request):
223232
"required": ["pairs"],
224233
}
225234
},
226-
responses={200: {"type": "array", "items": {"type": "object"}}},
235+
responses={
236+
200: {
237+
"description": "Gene info for matched gene/strain pairs.",
238+
"type": "array",
239+
"items": {
240+
"type": "object",
241+
"properties": {
242+
"gene": {"type": "string"},
243+
"locus_tag": {"type": "string"},
244+
"genome_id": {"type": "string"},
245+
"protein": {"type": "string"},
246+
"species": {"type": "string"},
247+
"pangenome_analysis": {"type": "string"},
248+
"start_position": {"type": "integer"},
249+
"end_position": {"type": "integer"},
250+
"nucleotide_seq": {"type": "string"},
251+
"aminoacid_seq": {"type": "string"},
252+
"url": {"type": "string", "format": "uri"},
253+
},
254+
},
255+
},
256+
400: {"description": "Invalid or empty input."},
257+
500: {"description": "Unexpected server error."},
258+
},
227259
examples=[
228260
OpenApiExample(
229261
"Example request",
@@ -242,20 +274,23 @@ def query_by_pair(request):
242274
pairs = [pairs]
243275

244276
if not pairs:
245-
return Response({
246-
"count": 0,
247-
"message": "No gene/strain pairs provided",
248-
})
277+
return Response(
278+
{"message": "pairs must be a non-empty list"},
279+
status=status.HTTP_400_BAD_REQUEST,
280+
)
249281

250-
clean_pairs = [
251-
{"gene": p["gene"], "genome_id": p["strain"]}
252-
for p in pairs
253-
if "gene" in p and "strain" in p
254-
]
282+
seen = set()
283+
clean_pairs = []
284+
for p in pairs:
285+
if "gene" in p and "strain" in p:
286+
key = (p["gene"], p["strain"])
287+
if key not in seen:
288+
seen.add(key)
289+
clean_pairs.append({"gene": p["gene"], "genome_id": p["strain"]})
255290
if not clean_pairs:
256-
return Response({
257-
"count": 0,
258-
"message": "Each pair must contain both 'gene' and 'strain'",
291+
return Response(
292+
{"message": "Each pair must contain both 'gene' and 'strain'"},
293+
status=status.HTTP_400_BAD_REQUEST,
259294
})
260295

261296
genes = {p["gene"] for p in clean_pairs}
@@ -299,7 +334,12 @@ def query_by_pair(request):
299334
@extend_schema(
300335
tags=["Genes"],
301336
summary="Query by gene names",
302-
description="Look up detailed gene info by a list of gene names.",
337+
description=(
338+
"Look up detailed gene info by a list of gene names. "
339+
"Accepts JSON body: {\"ids\": [\"geneA\", \"geneB\"]}. "
340+
"Returns 400 if the ids list is empty or missing. "
341+
"IDs not found in the database are silently omitted from the response."
342+
),
303343
request={
304344
"application/json": {
305345
"type": "object",
@@ -312,7 +352,30 @@ def query_by_pair(request):
312352
"required": ["ids"],
313353
}
314354
},
315-
responses={200: {"type": "array", "items": {"type": "object"}}},
355+
responses={
356+
200: {
357+
"description": "Gene info for matched gene names.",
358+
"type": "array",
359+
"items": {
360+
"type": "object",
361+
"properties": {
362+
"gene": {"type": "string"},
363+
"locus_tag": {"type": "string"},
364+
"genome_id": {"type": "string"},
365+
"protein": {"type": "string"},
366+
"species": {"type": "string"},
367+
"pangenome_analysis": {"type": "string"},
368+
"start_position": {"type": "integer"},
369+
"end_position": {"type": "integer"},
370+
"nucleotide_seq": {"type": "string"},
371+
"aminoacid_seq": {"type": "string"},
372+
"url": {"type": "string", "format": "uri"},
373+
},
374+
},
375+
},
376+
400: {"description": "Invalid or empty input."},
377+
500: {"description": "Unexpected server error."},
378+
},
316379
examples=[
317380
OpenApiExample(
318381
"Example request",
@@ -355,7 +418,12 @@ def query_by_gene(request):
355418
@extend_schema(
356419
tags=["Strains"],
357420
summary="Query by strain IDs",
358-
description="Look up genome info by a list of genome IDs.",
421+
description=(
422+
"Look up genome info by a list of genome IDs. "
423+
"Accepts JSON body: {\"ids\": [\"GCF_...\", \"GCF_...\"]}. "
424+
"Returns 400 if the ids list is empty or missing. "
425+
"IDs not found in the database are silently omitted from the response."
426+
),
359427
request={
360428
"application/json": {
361429
"type": "object",
@@ -368,7 +436,31 @@ def query_by_gene(request):
368436
"required": ["ids"],
369437
}
370438
},
371-
responses={200: {"type": "array", "items": {"type": "object"}}},
439+
responses={
440+
200: {
441+
"description": "Genome info for matched strain IDs.",
442+
"type": "array",
443+
"items": {
444+
"type": "object",
445+
"properties": {
446+
"genome_id": {"type": "string"},
447+
"strain": {"type": "string"},
448+
"species": {"type": "string"},
449+
"pangenome_analysis": {"type": "string"},
450+
"gc_content": {"type": "number"},
451+
"genome_len": {"type": "integer"},
452+
"gene_class_distribution": {"type": "array", "items": {"type": "integer"}},
453+
"phylo_group": {"type": "string"},
454+
"isolation_source": {"type": "string"},
455+
"country": {"type": "string"},
456+
"geo_loc_name": {"type": "string"},
457+
"url": {"type": "string", "format": "uri"},
458+
},
459+
},
460+
},
461+
400: {"description": "Invalid or empty input."},
462+
500: {"description": "Unexpected server error."},
463+
},
372464
examples=[
373465
OpenApiExample(
374466
"Example request",

0 commit comments

Comments
 (0)