Skip to content

Commit b9fd4a6

Browse files
authored
Merge pull request #11 from CIAT-DAPA/develop
Develop
2 parents 6a686e2 + 2f70b69 commit b9fd4a6

File tree

5 files changed

+337
-4
lines changed

5 files changed

+337
-4
lines changed

src/api.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
from flask_swagger_ui import get_swaggerui_blueprint
88

99
from modules.country import Countries
10+
from modules.projects import Projects
1011
from modules.crops import Crops, Groups
11-
from modules.accessions import AccessionsByIDCrop, AccessionsByIDGroup
12+
from modules.accessions import AccessionsByIDCrop,AccessionsByIDCropProject, AccessionsByIDGroup
1213

1314
app = Flask(__name__)
1415
CORS(app)
@@ -32,10 +33,12 @@ def send_static(path):
3233
return send_from_directory('static',path)
3334

3435
api.add_resource(Countries, '/api/v1/countries')
36+
api.add_resource(Projects, '/api/v1/projects')
3537
api.add_resource(Crops, '/api/v1/crops')
3638
api.add_resource(Groups, '/api/v1/groups')
3739
api.add_resource(AccessionsByIDCrop, '/api/v1/accessionsbyidcrop')
3840
api.add_resource(AccessionsByIDGroup, '/api/v1/accessionsbyidgroup')
41+
api.add_resource(AccessionsByIDCropProject, '/api/v1/accessionsbyidcropproject')
3942

4043

4144

src/modules/accessions.py

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import Flask, jsonify, request
22
from flask_restful import Resource
3-
from ormgap import Crop, Group, Accession, Country
3+
from ormgap import Crop, Group, Accession, Country, Project
44
import re
55

66
def is_valid_object_id(value):
@@ -12,6 +12,138 @@ def is_valid_object_id(value):
1212
pattern = re.compile("[0-9a-f]{24}")
1313
return pattern.match(value) is not None
1414

15+
class AccessionsByIDCropProject(Resource):
16+
17+
def __init__(self):
18+
super().__init__()
19+
20+
def get(self):
21+
"""
22+
Get accessions from database by crop id(s), iso, and project ext_id
23+
---
24+
parameters:
25+
- name: id
26+
in: query
27+
description: Crop id(s)
28+
required: true
29+
type: string
30+
example: 6035f5e5c6be2f14d07d6c7d or 6035f5e5c6be2f14d07d6c7d,6035f5e5c6be2f14d07d6c7e
31+
- name: iso
32+
in: query
33+
description: ISO code of the country
34+
required: true
35+
type: string
36+
example: KE
37+
- name: project
38+
in: query
39+
description: The external project identifier
40+
required: true
41+
type: string
42+
example: "lga" or "bolder"
43+
responses:
44+
200:
45+
description: List of accessions based on provided filters
46+
schema:
47+
id: Accession
48+
properties:
49+
id:
50+
type: string
51+
description: Id of the accession
52+
species_name:
53+
type: string
54+
description: Name of the species of the accession.
55+
crop:
56+
type: string
57+
description: Id of the crop that the accession belongs to.
58+
landrace_group:
59+
type: string
60+
description: Id group that the landrace belongs to.
61+
country:
62+
type: string
63+
description: Iso2 country to which the accession belongs.
64+
institution_name:
65+
type: string
66+
description: Name of the institution that holds the accession.
67+
source_database:
68+
type: string
69+
description: Name of the database where the accession was originally stored. Optional.
70+
latitude:
71+
type: float
72+
description: Latitude of the geographical location where the accession was collected.
73+
longitude:
74+
type: float
75+
description: Longitude of the geographical location where the accession was collected.
76+
accession_id:
77+
type: string
78+
description: The identifier of the accession in source database.
79+
ext_id:
80+
type: string
81+
description: External identifier for the accession.
82+
other_attributes:
83+
type: dict
84+
description: Additional attributes of the accession.
85+
"""
86+
# Retrieve the parameters
87+
crop_ids = request.args.get('id') # Comma separated crop ids
88+
iso = request.args.get('iso') # Country iso
89+
project_ext_id = request.args.get('project') # Project external id
90+
91+
# Validate inputs
92+
if not crop_ids or not iso or not project_ext_id:
93+
return {'error': 'Crop ID(s), Iso 2, and Project ext_id are required'}, 400
94+
95+
# Convert iso to uppercase and process crop_ids
96+
iso = iso.upper()
97+
crop_id_list = list(dict.fromkeys(crop_ids.replace(" ", "").split(',')))
98+
99+
# Get country by iso
100+
country = Country.objects(iso_2=iso).first()
101+
if not country:
102+
return {"error": f"Country with iso 2 '{iso}' not found"}, 404
103+
104+
# Get project by ext_id
105+
project = Project.objects(ext_id=project_ext_id).first()
106+
if not project:
107+
return {"error": f"Project with ext_id '{project_ext_id}' not found"}, 404
108+
109+
# Prepare list to store results
110+
json_data = []
111+
112+
for crop_id in crop_id_list:
113+
if is_valid_object_id(crop_id):
114+
crop = Crop.objects(id=crop_id).first()
115+
if crop:
116+
# Filter accessions by crop, country, and project
117+
accessions = Accession.objects(crop=crop, country=country, project=project)
118+
crop_data = {
119+
"crop_id": str(crop.id),
120+
"accessions": [
121+
{
122+
"id": str(x.id),
123+
"species_name": x.species_name,
124+
"ext_id": x.ext_id,
125+
"crop": str(x.crop.id),
126+
"landrace_group": str(x.landrace_group.id) if x.landrace_group else None,
127+
"country": x.country.iso_2,
128+
"institution_name": x.institution_name,
129+
"source_database": x.source_database,
130+
"latitude": x.latitude,
131+
"longitude": x.longitude,
132+
"accession_id": x.accession_id,
133+
"other_attributes": x.other_attributes
134+
}
135+
for x in accessions
136+
]
137+
}
138+
json_data.append(crop_data)
139+
else:
140+
json_data.append({"crop_id": crop_id, "error": "Crop not found"})
141+
else:
142+
json_data.append({"crop_id": crop_id, "error": "Invalid crop ID"})
143+
144+
return jsonify(json_data)
145+
146+
15147
class AccessionsByIDCrop(Resource):
16148

17149
def __init__(self):
@@ -105,7 +237,7 @@ def get(self):
105237
crop_data = {"crop_id": str(crop.id),
106238
"accessions": [{"id": str(x.id), "species_name": x.species_name,
107239
"ext_id": x.ext_id, "crop": str(x.crop.id),
108-
"landrace_group":str(x.landrace_group.id),
240+
"landrace_group": str(x.landrace_group.id) if x.landrace_group else None,
109241
"country": x.country.iso_2,
110242
"institution_name":x.institution_name,
111243
"source_database":x.source_database,
@@ -215,7 +347,7 @@ def get(self):
215347
group_data = {"group_id": str(group.id),
216348
"accessions": [{"id": str(x.id), "species_name": x.species_name,
217349
"ext_id": x.ext_id, "crop": str(x.crop.id),
218-
"landrace_group":str(x.landrace_group.id),
350+
"landrace_group": str(x.landrace_group.id) if x.landrace_group else None,
219351
"country": x.country.iso_2,
220352
"institution_name":x.institution_name,
221353
"source_database":x.source_database,

src/modules/projects.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from flask import Flask, jsonify
2+
from flask_restful import Resource
3+
from ormgap import Project
4+
5+
class Projects(Resource):
6+
7+
def __init__(self):
8+
super().__init__()
9+
10+
def get(self):
11+
"""
12+
Get all projects from database
13+
---
14+
responses:
15+
200:
16+
description:
17+
schema:
18+
id: Project
19+
properties:
20+
id:
21+
type: string
22+
description: Project ID
23+
name:
24+
type: string
25+
description: Project name
26+
ext_id:
27+
type: string
28+
description: External identifier for the project
29+
"""
30+
q_set = Project.objects()
31+
json_data = [{"id": str(x.id), "name": x.name, "ext_id": x.ext_id} for x in q_set]
32+
return json_data

src/static/swagger.json

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
}
1616
],
1717
"tags": [
18+
{
19+
"name": "project",
20+
"description": "Everything about projects"
21+
},
1822
{
1923
"name": "country",
2024
"description": "Everything about countries"
@@ -29,6 +33,31 @@
2933
}
3034
],
3135
"paths": {
36+
"/api/v1/projects": {
37+
"get": {
38+
"tags": [
39+
"project"
40+
],
41+
"summary": "List projects",
42+
"description": "Returns a list of all projects",
43+
"operationId": "getProjects",
44+
"responses": {
45+
"200": {
46+
"description": "successful operation",
47+
"content": {
48+
"application/json": {
49+
"schema": {
50+
"type": "array",
51+
"items": {
52+
"$ref": "#/components/schemas/Project"
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
},
3261
"/api/v1/countries": {
3362
"get": {
3463
"tags": [
@@ -205,6 +234,75 @@
205234
}
206235
}
207236
},
237+
"/api/v1/accessionsbyidcropproject": {
238+
"get": {
239+
"tags": [
240+
"accession"
241+
],
242+
"summary": "Finds Accessions by Crop Id(s), Iso 2 country and Project Ext_id",
243+
"description": "Multiple Crop Ids can be provided with comma separated strings",
244+
"operationId": "getAccessionsbyidcropproject",
245+
"parameters": [
246+
{
247+
"name": "id",
248+
"in": "query",
249+
"description": "Crop IDs to filter by. e.g:64094b58b307071b4e72e908,64094b58b307071b4e72e907",
250+
"required": true,
251+
"explode": true,
252+
"schema": {
253+
"type": "array",
254+
"items": {
255+
"type": "string"
256+
}
257+
}
258+
},
259+
{
260+
"name": "iso",
261+
"in": "query",
262+
"description": "Iso 2 country to filter by. e.g:AR",
263+
"required": true,
264+
"explode": true,
265+
"schema": {
266+
"type": "string"
267+
}
268+
},
269+
{
270+
"name": "project",
271+
"in": "query",
272+
"description": "Project ext_id to filter by. e.g:bolder",
273+
"required": true,
274+
"explode": true,
275+
"schema": {
276+
"type": "string"
277+
}
278+
}
279+
],
280+
"responses": {
281+
"200": {
282+
"description": "successful operation",
283+
"content": {
284+
"application/json": {
285+
"schema": {
286+
"type": "array",
287+
"items": {
288+
"$ref": "#/components/schemas/AccessionByIdCropProject"
289+
}
290+
}
291+
}
292+
}
293+
},
294+
"400": {
295+
"$ref": "#/components/responses/BadRequest"
296+
},
297+
"404": {
298+
"$ref": "#/components/responses/NotFound"
299+
},
300+
"500": {
301+
"$ref": "#/components/responses/ServerError"
302+
}
303+
}
304+
}
305+
},
208306
"/api/v1/accessionsbyidgroup": {
209307
"get": {
210308
"tags": [
@@ -290,6 +388,29 @@
290388
"name": "country"
291389
}
292390
},
391+
"Project": {
392+
"type": "object",
393+
"properties": {
394+
"id": {
395+
"type": "string",
396+
"description": "Id Project.",
397+
"example": "640945c244d7c73ce4090301"
398+
},
399+
"name": {
400+
"type": "string",
401+
"description": "Project name.",
402+
"example": "BOLDER"
403+
},
404+
"ext_id": {
405+
"type": "string",
406+
"description": "Extern Id to identify for project.",
407+
"example": "bolder"
408+
}
409+
},
410+
"xml": {
411+
"name": "project"
412+
}
413+
},
293414
"Crop": {
294415
"type": "object",
295416
"properties": {

0 commit comments

Comments
 (0)