@@ -814,7 +814,161 @@ def otts2identifiers():
814
814
headers = colname_map ,
815
815
ids = ret )
816
816
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
+ )))
817
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_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
+
818
972
#PRIVATE FUNCTIONS
819
973
820
974
0 commit comments