Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 16 additions & 3 deletions api/resources/fastpheno.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,27 @@ def get(self, seq_id, band):
class FastPhenoGenotypeTimeSeries(Resource):
@fastpheno.param("tree_site_id", _in="path", default="619")
@fastpheno.param("band", _in="path", default="398nm")
@fastpheno.param("site", _in="query", required=False, default="Pintendre")
def get(self, tree_site_id, band):
"""Returns AVG and STDDEV of band values per flight date across all trees sharing a
genotype (e.g. '619' matches all tree_site_id values starting with '619.'). Intended
for genotype-level time series plotting."""
for genotype-level time series plotting. Optionally filter by site name."""
tree_site_id = str(escape(tree_site_id))
band = str(escape(band))
site = request.args.get("site")

if not re.search(r"^[a-zA-Z0-9]{1,10}$", tree_site_id):
return BARUtils.error_exit("Invalid tree site ID"), 400

if not re.search(r"^[a-zA-Z0-9_]{1,20}$", band):
return BARUtils.error_exit("Invalid band"), 400

rows = db.session.execute(
if site is not None:
site = str(escape(site)).capitalize()
if not re.search(r"^[a-zA-Z]{1,15}$", site):
return BARUtils.error_exit("Invalid site name"), 400

query = (
db.select(
Flights.flight_date,
Flights.flights_pk,
Expand All @@ -198,13 +205,19 @@ def get(self, tree_site_id, band):
)
.join(Trees, Bands.trees_pk == Trees.trees_pk)
.join(Flights, Bands.flights_pk == Flights.flights_pk)
.join(Sites, Flights.sites_pk == Sites.sites_pk)
.where(
db.or_(Trees.tree_site_id == tree_site_id, Trees.tree_site_id.like(f"{tree_site_id}.%")),
Bands.band == band,
)
.group_by(Flights.flights_pk, Flights.flight_date)
.order_by(Flights.flight_date)
).all()
)

if site is not None:
query = query.where(Sites.site_name == site)

rows = db.session.execute(query).all()

if len(rows) == 0:
return BARUtils.error_exit("No data found for the given parameters"), 400
Expand Down
15 changes: 15 additions & 0 deletions tests/resources/test_fastpheno.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ def test_timeseries_genotype_aggregate(self):
}
self.assertEqual(response.json, expected)

# With site filter
response = self.app_client.get("/fastpheno/timeseries/genotype/619/398nm/aggregate?site=Pintendre")
self.assertEqual(response.json["wasSuccessful"], True)
self.assertEqual(len(response.json["data"]), 1)

# No data when site has no matching trees
response = self.app_client.get("/fastpheno/timeseries/genotype/619/398nm/aggregate?site=Pickering")
expected = {"wasSuccessful": False, "error": "No data found for the given parameters"}
self.assertEqual(response.json, expected)

# No data for valid but non-existent genotype
response = self.app_client.get("/fastpheno/timeseries/genotype/9999/398nm/aggregate")
expected = {"wasSuccessful": False, "error": "No data found for the given parameters"}
Expand All @@ -231,6 +241,11 @@ def test_timeseries_genotype_aggregate(self):
expected = {"wasSuccessful": False, "error": "Invalid band"}
self.assertEqual(response.json, expected)

# Invalid site name
response = self.app_client.get("/fastpheno/timeseries/genotype/619/398nm/aggregate?site=123")
expected = {"wasSuccessful": False, "error": "Invalid site name"}
self.assertEqual(response.json, expected)

def test_sites(self):
"""Tests GET /fastpheno/sites"""
response = self.app_client.get("/fastpheno/sites")
Expand Down
Loading