1111from ckanext .better_stats .metrics .base import MetricBase
1212
1313
14+ class OrganizationHierarchyMetric (MetricBase ):
15+ """Tree chart of organization parent-child relationships via ckanext-hierarchy.
16+
17+ This metric is only available if `ckanext-hierarchy` is installed and enabled.
18+ """
19+
20+ supported_visualizations : ClassVar [list [const .VisualizationType ]] = [
21+ const .VisualizationType .CHART ,
22+ ]
23+ default_visualization : ClassVar [const .VisualizationType ] = const .VisualizationType .CHART
24+ icon : ClassVar [str ] = "fa-solid fa-sitemap"
25+ supported_export_formats : ClassVar [list [str ]] = ["image" ]
26+
27+ def __init__ (self ) -> None :
28+ super ().__init__ (
29+ name = "organization_hierarchy" ,
30+ title = tk ._ ("Organization Hierarchy" ),
31+ description = tk ._ ("Tree view of organization parent-child relationships" ),
32+ order = 14 ,
33+ grid_size = "full" ,
34+ )
35+
36+ def get_data (self ) -> list [dict [str , Any ]]:
37+ return tk .h .group_tree (type_ = "organization" )
38+
39+ def _to_echarts_node (self , node : dict [str , Any ]) -> dict [str , Any ]:
40+ name = node ["name" ]
41+ url = tk .url_for ("organization.read" , id = name )
42+ converted : dict [str , Any ] = {
43+ "name" : node .get ("title" ) or name or "Unknown" ,
44+ "value" : (f'<a href="{ url } " target="_blank" style="color:inherit;">{ tk ._ ("View" )} →</a>' ),
45+ }
46+ children = node .get ("children" , [])
47+
48+ if children :
49+ converted ["children" ] = [self ._to_echarts_node (c ) for c in children ]
50+
51+ return converted
52+
53+ def get_chart_data (self ) -> dict [str , Any ]:
54+ roots = self .get_data ()
55+
56+ if not roots :
57+ return {}
58+
59+ if len (roots ) == 1 :
60+ tree_data = self ._to_echarts_node (roots [0 ])
61+ else :
62+ tree_data = {
63+ "name" : tk ._ ("Organizations" ),
64+ "children" : [self ._to_echarts_node (r ) for r in roots ],
65+ }
66+
67+ return {
68+ "tooltip" : {
69+ "trigger" : "item" ,
70+ "triggerOn" : "mousemove" ,
71+ "enterable" : True ,
72+ "formatter" : "{b}<br/>{c}" ,
73+ "_htmlTooltip" : True ,
74+ },
75+ "series" : [
76+ {
77+ "type" : "tree" ,
78+ "data" : [tree_data ],
79+ "top" : "5%" ,
80+ "left" : "5%" ,
81+ "bottom" : "5%" ,
82+ "right" : "5%" ,
83+ "symbolSize" : 10 ,
84+ "lineStyle" : {"width" : 1 },
85+ "label" : {
86+ "position" : "left" ,
87+ "verticalAlign" : "middle" ,
88+ "align" : "right" ,
89+ "fontSize" : 13 ,
90+ "width" : 160 ,
91+ "overflow" : "truncate" ,
92+ "ellipsis" : "…" ,
93+ },
94+ "leaves" : {
95+ "label" : {
96+ "position" : "right" ,
97+ "verticalAlign" : "middle" ,
98+ "align" : "left" ,
99+ "width" : 160 ,
100+ "overflow" : "truncate" ,
101+ "ellipsis" : "…" ,
102+ }
103+ },
104+ "emphasis" : {"focus" : "descendant" },
105+ "expandAndCollapse" : False ,
106+ "animationDuration" : 350 ,
107+ "animationDurationUpdate" : 350 ,
108+ "roam" : True ,
109+ }
110+ ],
111+ }
112+
113+
14114class OrganizationCountMetric (MetricBase ):
15115 """Total number of active organizations with a monthly creation trend."""
16116
@@ -59,7 +159,10 @@ def get_chart_data(self) -> dict[str, Any]:
59159 )
60160 return {
61161 "tooltip" : {"trigger" : "axis" },
62- "xAxis" : {"type" : "category" , "data" : [row .month .strftime ("%Y-%m" ) for row in rows ]},
162+ "xAxis" : {
163+ "type" : "category" ,
164+ "data" : [row .month .strftime ("%Y-%m" ) for row in rows ],
165+ },
63166 "yAxis" : {"type" : "value" , "minInterval" : 1 },
64167 "series" : [{"type" : "line" , "data" : [row .count for row in rows ], "smooth" : True }],
65168 }
@@ -254,7 +357,10 @@ def get_data(self) -> list[dict[str, Any]]:
254357 )
255358 return [
256359 {
257- "organization" : {"label" : row .title , "url" : tk .url_for ("organization.read" , id = row .name )},
360+ "organization" : {
361+ "label" : row .title ,
362+ "url" : tk .url_for ("organization.read" , id = row .name ),
363+ },
258364 "datasets" : row .datasets ,
259365 "resources" : row .resources ,
260366 "members" : row .members ,
@@ -273,7 +379,10 @@ def get_table_data(self) -> dict[str, Any]:
273379 ],
274380 "rows" : [
275381 [
276- {"text" : item ["organization" ]["label" ], "url" : item ["organization" ]["url" ]},
382+ {
383+ "text" : item ["organization" ]["label" ],
384+ "url" : item ["organization" ]["url" ],
385+ },
277386 item ["datasets" ],
278387 item ["resources" ],
279388 item ["members" ],
@@ -401,7 +510,13 @@ def get_chart_data(self) -> dict[str, Any]:
401510 "series" : [
402511 {
403512 "type" : "treemap" ,
404- "data" : [{"name" : item ["organization" ] or "Unknown" , "value" : item ["count" ]} for item in data ],
513+ "data" : [
514+ {
515+ "name" : item ["organization" ] or "Unknown" ,
516+ "value" : item ["count" ],
517+ }
518+ for item in data
519+ ],
405520 "label" : {"show" : True , "formatter" : "{b}" },
406521 "itemStyle" : {"borderColor" : "#fff" },
407522 "roam" : False ,
@@ -414,4 +529,4 @@ def get_table_data(self) -> dict[str, Any]:
414529 return {
415530 "headers" : [tk ._ ("Organization" ), tk ._ ("Datasets" )],
416531 "rows" : [[{"text" : item ["organization" ], "url" : item ["url" ]}, item ["count" ]] for item in data ],
417- }
532+ }
0 commit comments