@@ -814,7 +814,161 @@ 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+ )))
817841
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_id = {}
877+ ret = dict (
878+ step = step ,
879+ date_MYA = date ,
880+ n_spp = n_leaves ,
881+ name = row .name ,
882+ )
883+ if include_otts :
884+ ret ["input_ott" ] = ott
885+
886+ data_by_ott = {ott : ret }
887+ data_by_name = {}
888+
889+ while row .parent >= 0 :
890+ step += 1
891+ rows = db (db .ordered_nodes .id == row .parent ).select (
892+ db .ordered_nodes .id ,
893+ db .ordered_nodes .parent ,
894+ db .ordered_nodes .name ,
895+ db .ordered_nodes .ott ,
896+ db .ordered_nodes .age ,
897+ db .ordered_nodes .leaf_lft ,
898+ db .ordered_nodes .leaf_rgt ,
899+ )
900+ if not rows :
901+ break
902+ row = rows .first ()
903+ n_leaves = row .leaf_rgt - row .leaf_lft + 1
904+ row_data = dict (
905+ step = step ,
906+ date_MYA = None if row .age is None else round (row .age , 1 ),
907+ n_spp = n_leaves ,
908+ tracked_branch_n_spp = prev_n_leaves ,
909+ )
910+ if prev_leaf_lft == row .leaf_lft :
911+ # the tracked branch is the left-hand branch from this node
912+ data_by_sib_id [row .id + prev_n_leaves ] = row_data
913+ else :
914+ # the tracked branch is the right-hand branch from this node
915+ data_by_sib_id [row .id + 1 ] = row_data
916+
917+ row_data ["name" ] = row .name if row .name and not row .name .endswith ("_" ) else None
918+ row_data ["vernacular" ] = None
919+ if include_otts :
920+ row_data ["ott" ] = row .ott
921+ row_data ["sib_branches" ]= {"n_spp" : n_leaves - prev_n_leaves }
922+
923+ prev_n_leaves = n_leaves
924+ ancestors .append (row_data )
925+ if row .ott is not None :
926+ data_by_ott [row .ott ] = row_data
927+ elif row .name is not None :
928+ data_by_name [row .name ] = row_data
929+
930+ # Get data on the siblings
931+ # TODO - what if a sib is a leaf?
932+ if data_by_sib_id :
933+ rows = db (db .ordered_nodes .id .belongs (data_by_sib_id )).select (
934+ db .ordered_nodes .id , db .ordered_nodes .ott , db .ordered_nodes .name )
935+ if rows :
936+ for row in rows :
937+ parent_data = data_by_sib_id [row .id ]
938+ # if name ends in _ it's not a correct scientific name, but a OZ nickname
939+ if row .name and not row .name .endswith ("_" ):
940+ parent_data ["sib_branches" ]["name" ] = row .name
941+ if row .ott is not None :
942+ data_by_ott [row .ott ] = parent_data ["sib_branches" ]
943+ elif row .name is not None :
944+ data_by_name [row .name ] = parent_data ["sib_branches" ]
945+
946+
947+ for ott , vernacular in OZfunc .get_common_names (
948+ data_by_ott .keys (),
949+ return_nulls = False ,
950+ prefer_short_name = False ,
951+ include_unpreferred = False ,
952+ return_all = False ,
953+ lang = lang ,
954+ ).items ():
955+ data_by_ott [ott ]["vernacular" ] = vernacular
956+
957+ for name , vernacular in OZfunc .get_common_names (
958+ data_by_name .keys (),
959+ OTT = False ,
960+ return_nulls = False ,
961+ prefer_short_name = False ,
962+ include_unpreferred = False ,
963+ return_all = False ,
964+ lang = lang ,
965+ ).items ():
966+ data_by_name [name ]["vernacular" ] = vernacular
967+ ret ["ancestors" ] = ancestors
968+
969+ return ret
970+
971+
818972#PRIVATE FUNCTIONS
819973
820974
0 commit comments