11
2- import yaml
2+ import re
3+ import traceback
34import typer
4- from pprint import pprint
5+
6+ from pprint import pformat
7+
8+ from lkml2cube .parser .views import parse_view
9+
10+ snake_case = r'\{([a-zA-Z]+(?:_[a-zA-Z]+)*\.[a-zA-Z]+(?:_[a-zA-Z]+)*)\}'
11+
12+ def snakify (s ):
13+ return '_' .join (
14+ re .sub ('([A-Z][a-z]+)' , r' \1' ,
15+ re .sub ('([A-Z]+)' , r' \1' ,
16+ s .replace ('-' , ' ' ))).split ()
17+ ).lower ()
18+
19+ def build_cube_name_look_up (cube_def ):
20+ if 'cube_name_look_up' in cube_def :
21+ return
22+ cube_name_look_up = {}
23+ for cube_element in cube_def ['cubes' ]:
24+ cube_name_look_up [cube_element ['name' ]] = cube_element
25+ cube_def ['cube_name_look_up' ] = cube_name_look_up
26+
27+ def get_cube_from_cube_def (cube_def , cube_name ):
28+ if 'cube_name_look_up' not in cube_def :
29+ build_cube_name_look_up (cube_def )
30+ if cube_name in cube_def ['cube_name_look_up' ]:
31+ return cube_def ['cube_name_look_up' ][cube_name ]
32+ return None
33+
34+ def get_cube_names_from_join_condition (join_condition ):
35+ return [cube .split ('.' )[0 ] for cube in re .findall (snake_case , join_condition )]
36+
37+ def traverse_graph (join_paths , cube_left , cube_right ):
38+ # Create a queue for BFS
39+ queue = []
40+ queue .append ([cube_left ])
41+
42+ while queue :
43+ #Dequeue a vertex from queue
44+ tmp_path = queue .pop (0 )
45+ # If this adjacent node is the destination node,
46+ # then return true
47+ last_node = tmp_path [len (tmp_path )- 1 ]
48+ if last_node == cube_right :
49+ return '.' .join (tmp_path )
50+ # Else, continue to do BFS
51+ if last_node in join_paths :
52+ for cube in join_paths [last_node ]:
53+ if cube not in tmp_path :
54+ new_path = []
55+ new_path = tmp_path + [cube ]
56+ queue .append (new_path )
57+
58+ typer .echo (f'Cubes are not reachable: { cube_left } , { cube_right } ' )
59+ return '.' .join (cube_left , cube_right )
60+
61+
62+ def generate_cube_joins (cube_def , lookml_model ):
63+ for explore in lookml_model ['explores' ]:
64+ if 'joins' not in explore :
65+ continue
66+
67+ for join_element in explore ['joins' ]:
68+ try :
69+ cube_right = join_element ['name' ]
70+
71+ joined_cubes = [cube for cube in get_cube_names_from_join_condition (join_element ['sql_on' ]) if cube != cube_right ]
72+ if joined_cubes :
73+ if 'from' in join_element :
74+ cube = {
75+ 'name' : cube_right ,
76+ 'extends' : join_element ['from' ],
77+ 'shown' : False ,
78+ }
79+ cube_def ['cubes' ].append (cube )
80+ else :
81+ cube = get_cube_from_cube_def (cube_def , cube_right )
82+
83+ join_condition = join_element ['sql_on' ]
84+
85+ if 'joins' not in cube :
86+ cube ['joins' ] = []
87+
88+ cube ['joins' ].append ({
89+ 'name' : joined_cubes [0 ],
90+ 'sql' : join_condition ,
91+ 'relationship' : join_element ['relationship' ]
92+ })
93+ except Exception :
94+ typer .echo (f'Error while parsing explore: { pformat (explore )} ' )
95+ typer .echo (traceback .format_exc ())
96+
97+ return cube_def
98+
99+ def generate_cube_views (cube_def , lookml_model ):
100+ if 'views' not in cube_def :
101+ cube_def ['views' ] = []
102+ for explore in lookml_model ['explores' ]:
103+ try :
104+ central_cube = explore ['name' ]
105+ view_name = snakify (explore ['label' ])
106+ view = {
107+ 'name' : view_name ,
108+ 'description' : explore ['label' ],
109+ 'cubes' : [{
110+ 'join_path' : central_cube ,
111+ 'includes' : "*" ,
112+ 'alias' : view_name
113+ }]
114+ }
115+
116+ if 'joins' not in explore :
117+ cube_def ['views' ].append (view )
118+ continue
119+ # Create Graph
120+ join_paths = {}
121+ for join_element in explore ['joins' ]:
122+ cube_right = join_element ['name' ]
123+ cube_left = [cube for cube in get_cube_names_from_join_condition (join_element ['sql_on' ]) if cube != cube_right ][0 ]
124+
125+ if cube_left in join_paths :
126+ join_paths [cube_left ].append (cube_right )
127+ else :
128+ join_paths [cube_left ] = [cube_right ]
129+ # traverse graph
130+ for join_element in explore ['joins' ]:
131+ cube_right = join_element ['name' ]
132+ join_path = {
133+ 'join_path' : traverse_graph (join_paths , central_cube , cube_right ),
134+ 'includes' : "*" ,
135+ 'alias' : cube_right
136+ }
137+ view ['cubes' ].append (join_path )
138+
139+ # End
140+ cube_def ['views' ].append (view )
141+
142+ except Exception :
143+ typer .echo (f'Error while parsing explore: { pformat (explore )} ' )
144+ typer .echo (traceback .format_exc ())
145+ return cube_def
5146
6147
7148def parse_explores (lookml_model ):
8- return pprint (lookml_model )
149+ # First we read all possible lookml views.
150+ cube_def = parse_view (lookml_model , raise_when_views_not_present = False )
151+ if 'explores' not in lookml_model :
152+ raise Exception ('LookML explores are needed to generate Cube Views, no explore found in path.' )
153+ cube_def = generate_cube_joins (cube_def , lookml_model )
154+
155+ cube_def = generate_cube_views (cube_def , lookml_model )
156+
157+ return cube_def
0 commit comments