Skip to content

Commit e266ca5

Browse files
committed
Add API to get details of ancestors of a node
Something like this probably exists already in order to populate the "where am I" popup bar, but I have reimplemented something for Chris Haughton and Tiziana
1 parent 34e56ef commit e266ca5

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

controllers/API.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,174 @@ def otts2identifiers():
814814
headers=colname_map,
815815
ids=ret)
816816

817+
def ascend_ancestors():
818+
"""
819+
Used to return details of all the ancestors of a particular node, in particular
820+
- number of million years of each ancestor
821+
- the total number of species that are descendants on each side of the bifurcation
822+
- the name of the period/era/eon
823+
- a name (if it exists) for the ancestor
824+
825+
NB: finding the name for the other descendant group is a lot more tricky!
826+
827+
"""
828+
session.forget(response)
829+
lang = request.vars.lang or request.env.http_accept_language or 'en'
830+
response.headers["Access-Control-Allow-Origin"] = '*'
831+
832+
if "." not in request.env.path_info.split('/')[2]:
833+
request.extension = "json"
834+
response.view = request.controller + "/" + request.function + "." + request.extension
835+
836+
if request.vars.key is None:
837+
redirect(URL('error', vars=dict(
838+
code=400,
839+
text="Please use an API key (use 0 for the public API key)"
840+
)))
841+
842+
try:
843+
ott = int(request.vars.ott)
844+
except:
845+
redirect(URL('error', vars=dict(code=400, text="Invalid OTT")))
846+
847+
include_otts = request.vars.include_otts
848+
rows = db(db.ordered_leaves.ott == ott).select(
849+
db.ordered_leaves.id,
850+
db.ordered_leaves.parent,
851+
db.ordered_leaves.name,
852+
db.ordered_leaves.extinction_date,
853+
)
854+
if rows:
855+
row = rows.first()
856+
date = row.extinction_date or 0
857+
prev_n_leaves = n_leaves = 1
858+
prev_leaf_lft = row.id
859+
else:
860+
rows = db(db.ordered_nodes.ott == ott).select(
861+
db.ordered_nodes.parent,
862+
db.ordered_nodes.name,
863+
db.ordered_nodes.age,
864+
db.ordered_nodes.leaf_lft,
865+
db.ordered_nodes.leaf_rgt,
866+
)
867+
if not rows:
868+
redirect(URL('error', vars=dict(code=400, text="No matching OTT found")))
869+
row = rows.first()
870+
date = None if row.age is None else round(row.age, 1)
871+
prev_n_leaves = n_leaves = row.leaf_rgt - row.leaf_lft + 1
872+
prev_leaf_lft = row.leaf_lft
873+
874+
step = 0
875+
ancestors = []
876+
data_by_sib_node_id = {}
877+
data_by_sib_leaf_id = {}
878+
ret = dict(
879+
step=step,
880+
date_MYA=date,
881+
n_spp=n_leaves,
882+
name=row.name,
883+
)
884+
if include_otts:
885+
ret["input_ott"] = ott
886+
887+
data_by_ott = {ott: ret}
888+
data_by_name = {}
889+
890+
while row.parent >= 0:
891+
step += 1
892+
rows = db(db.ordered_nodes.id == row.parent).select(
893+
db.ordered_nodes.id,
894+
db.ordered_nodes.parent,
895+
db.ordered_nodes.name,
896+
db.ordered_nodes.ott,
897+
db.ordered_nodes.age,
898+
db.ordered_nodes.leaf_lft,
899+
db.ordered_nodes.leaf_rgt,
900+
)
901+
if not rows:
902+
break
903+
row = rows.first()
904+
n_leaves = row.leaf_rgt - row.leaf_lft + 1
905+
row_data = dict(
906+
step=step,
907+
date_MYA=None if row.age is None else round(row.age, 1),
908+
n_spp=n_leaves,
909+
tracked_branch_n_spp=prev_n_leaves,
910+
)
911+
sib_n_leaves = n_leaves - prev_n_leaves
912+
if prev_leaf_lft == row.leaf_lft:
913+
# the tracked branch is the left-hand branch from this node
914+
if sib_n_leaves > 1:
915+
data_by_sib_node_id[row.id + prev_n_leaves] = row_data
916+
else:
917+
data_by_sib_leaf_id[row.leaf_rgt] = row_data
918+
919+
else:
920+
# the tracked branch is the right-hand branch from this node
921+
if sib_n_leaves > 1:
922+
data_by_sib_node_id[row.id + 1] = row_data
923+
else:
924+
data_by_sib_leaf_id[row.leaf_lft] = row_data
925+
926+
927+
row_data["name"] = row.name if row.name and not row.name.endswith("_") else None
928+
row_data["vernacular"] = None
929+
if include_otts:
930+
row_data["ott"] = row.ott
931+
row_data["sib_branches"]={"n_spp": sib_n_leaves}
932+
933+
prev_n_leaves = n_leaves
934+
ancestors.append(row_data)
935+
if row.ott is not None:
936+
data_by_ott[row.ott] = row_data
937+
elif row.name is not None:
938+
data_by_name[row.name] = row_data
939+
940+
# Get data on the siblings
941+
for sib_data_dict, db_table in zip(
942+
(data_by_sib_node_id, data_by_sib_leaf_id),
943+
(db.ordered_nodes, db.ordered_leaves)
944+
):
945+
if len(sib_data_dict) > 0:
946+
rows = db(db_table.id.belongs(sib_data_dict.keys())).select(
947+
db_table.id, db_table.ott, db_table.name)
948+
if rows:
949+
for row in rows:
950+
parent_data = sib_data_dict[row.id]
951+
# if name ends in _ it's not the scientific name, but a OZ nickname
952+
if row.name and not row.name.endswith("_"):
953+
parent_data["sib_branches"]["name"] = row.name
954+
if row.ott is not None:
955+
data_by_ott[row.ott] = parent_data["sib_branches"]
956+
elif row.name is not None:
957+
data_by_name[row.name] = parent_data["sib_branches"]
958+
817959

960+
for ott, vernacular in OZfunc.get_common_names(
961+
data_by_ott.keys(),
962+
return_nulls=False,
963+
prefer_short_name=False,
964+
include_unpreferred=False,
965+
return_all=False,
966+
lang=lang,
967+
).items():
968+
data_by_ott[ott]["vernacular"] = vernacular
969+
970+
for name, vernacular in OZfunc.get_common_names(
971+
data_by_name.keys(),
972+
OTT=False,
973+
return_nulls=False,
974+
prefer_short_name=False,
975+
include_unpreferred=False,
976+
return_all=False,
977+
lang=lang,
978+
).items():
979+
data_by_name[name]["vernacular"] = vernacular
980+
ret["ancestors"] = ancestors
981+
982+
return ret
983+
984+
818985
#PRIVATE FUNCTIONS
819986

820987

0 commit comments

Comments
 (0)