mdl_convert.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import json
  2. from pathlib import Path
  3. import re
  4. CONVERSION = [
  5. "Name",
  6. "CognosSource",
  7. "CognosPackageDatasourceConnection",
  8. "DataSource",
  9. "OrgName",
  10. "Dimension",
  11. "Root",
  12. "Drill",
  13. "Levels",
  14. "Associations",
  15. "Category",
  16. "SpecialCategory",
  17. "MapDrills",
  18. "ViewName",
  19. "Measure",
  20. "Signon",
  21. "Cube",
  22. "CustomView",
  23. "CustomViewChildList",
  24. "SecurityNameSpace",
  25. "SecurityObject",
  26. "AllocationAdd",
  27. ]
  28. id_lookup = {}
  29. find_words = re.compile(r'("[^"]+"|\w+) ')
  30. def convert_block(block):
  31. block = block.replace("\n", "")
  32. block_type = block.split(" ")[0]
  33. words = find_words.findall(block)
  34. if len(words) < 3:
  35. return {"Type": block_type}
  36. result = {"Type": words[0], "ID": words[1], "Name": words[2].strip('"')}
  37. offset = 0
  38. for i in range(3, len(words), 2):
  39. if len(words) < i + offset + 2:
  40. break
  41. key = words[i + offset]
  42. if key in ["PackageReportSource", "Database"]:
  43. result[key] = {
  44. "ID": words[i + offset + 1],
  45. "Name": words[i + offset + 2].strip('"'),
  46. }
  47. offset += 1
  48. elif key in ["DimensionView"]:
  49. if key + "s" not in result:
  50. result[key + "s"] = []
  51. result[key + "s"].append(
  52. {"ID": words[i + offset + 1], "Name": words[i + offset + 2].strip('"')}
  53. )
  54. offset += 1
  55. elif key in ["MeasureInclude"]:
  56. if key + "s" not in result:
  57. result[key + "s"] = []
  58. result[key + "s"].append(
  59. {"ID": words[i + offset + 1], "Include": words[i + offset + 2]}
  60. )
  61. offset += 1
  62. elif key == "Calc":
  63. for j in range(i + offset + 1, len(words)):
  64. if words[j] in ["Sign", "Format", "Filtered"] or j == len(words) - 1:
  65. result["Calc"] = " ".join(words[i + offset + 1 : j])
  66. offset = j - i - 1
  67. break # for
  68. elif key == "EncryptedPW":
  69. result["EncryptedPW"] = words[i + offset + 1].strip('"')
  70. result["Salt"] = words[i + offset + 2].strip('"')
  71. offset += 1
  72. elif key == "AllocationAdd":
  73. if key + "s" not in result:
  74. result[key + "s"] = []
  75. result[key + "s"].append(
  76. {"Measure": words[i + offset + 2], "Type": words[i + offset + 4]}
  77. )
  78. offset += 3
  79. elif key in [
  80. "CustomViewList",
  81. "DrillThrough",
  82. "DeployLocations",
  83. "PowerCubeCustomViewList",
  84. "StartList",
  85. "TransientLevelList",
  86. ]:
  87. for j in range(i + offset + 1, len(words)):
  88. if words[j] in ["EndList"]:
  89. result[key] = " ".join(words[i + offset + 1 : j])
  90. offset = j - i - 1
  91. break # for
  92. # elif words[i + offset].isnumeric() or words[i + offset].startswith('"'):
  93. # offset += 1
  94. else:
  95. result[key] = words[i + offset + 1].strip('"')
  96. if block_type == "DataSource":
  97. result["Columns"] = []
  98. if block_type in ["OrgName", "Levels", "Measure"]:
  99. result["Associations"] = []
  100. if block_type == "Dimension":
  101. result["Root"] = {}
  102. result["Levels"] = []
  103. result["Categories"] = []
  104. result["SpecialCategories"] = []
  105. if block_type == "Root":
  106. result["Drill"] = {}
  107. if block_type == "Associations":
  108. result["Parent"] = 0
  109. if block_type == "CustomView":
  110. result["ChildList"] = {}
  111. if block_type == "SecurityNameSpace":
  112. result["Objects"] = []
  113. return result
  114. def remove_ids(nested):
  115. nested.pop("ID", "")
  116. nested.pop("DateDrill", "")
  117. nested.pop("Primary", "")
  118. nested.pop("Lastuse", "")
  119. nested.pop("AssociationContext", "")
  120. if (
  121. nested.get("Type", "") == "SpecialCategory"
  122. and "Label" in nested
  123. and "20" in nested["Label"]
  124. ):
  125. nested.pop("Label", "")
  126. for col in ["Parent", "Levels", "CustomViewList"]:
  127. if col not in nested:
  128. continue
  129. if col == "Levels" and (
  130. isinstance(nested["Levels"], list) or nested["Levels"] == "0"
  131. ):
  132. continue
  133. nested[col] = id_lookup.get(nested[col], {}).get("Name", "undefined")
  134. for child in nested.values():
  135. if isinstance(child, dict):
  136. remove_ids(child)
  137. if isinstance(child, list):
  138. for entry in child:
  139. remove_ids(entry)
  140. return nested
  141. def prepare_mdl_str(mdl_str):
  142. mdl_str = re.sub(r"\n+", "\n", mdl_str)
  143. mdl_str = re.sub(r"^\n?Name ", "ModelName 1 ", mdl_str)
  144. mdl_str = re.sub(r'\nLevels (\d+ [^"])', r"Levels \1", mdl_str)
  145. mdl_str = re.sub(r" Associations ", " \nAssociations ", mdl_str)
  146. mdl_str = re.sub(r'([^ ])""', r"\1'", mdl_str)
  147. mdl_str = re.sub(r'""([^ ])', r"'\1", mdl_str)
  148. tags = "|".join(CONVERSION)
  149. mdl_str = re.sub(r"\n(" + tags + r") ", r"\n\n\1 ", mdl_str)
  150. return mdl_str
  151. def group_mdl_blocks(converted):
  152. result = {
  153. "Model": {},
  154. "Connections": [],
  155. "DataSources": [],
  156. "Dimensions": [],
  157. "Measures": [],
  158. "Signons": [],
  159. "CustomViews": [],
  160. "Security": [],
  161. "Cubes": [],
  162. }
  163. types = [c["Type"] for c in converted]
  164. ids = [c.get("ID", "0") for c in converted]
  165. id_lookup.update(dict(zip(ids, converted)))
  166. current = None
  167. level_ids = []
  168. for c, t in zip(converted, types):
  169. if t in [""]:
  170. continue
  171. if (
  172. t in ["Category", "SpecialCategory"]
  173. and result["Dimensions"][-1]["Name"] == "Zeit"
  174. ):
  175. if t == "Category" or c["Name"][0].isnumeric():
  176. continue
  177. if t in ["ModelName"]:
  178. result["Model"] = c
  179. elif t in ["CognosSource", "CognosPackageDatasourceConnection"]:
  180. result["Connections"].append(c)
  181. elif t in ["DataSource"]:
  182. result["DataSources"].append(c)
  183. elif t in ["OrgName"]:
  184. result["DataSources"][-1]["Columns"].append(c)
  185. elif t in ["Dimension"]:
  186. level_ids = []
  187. result["Dimensions"].append(c)
  188. elif t in ["Root"]:
  189. result["Dimensions"][-1]["Root"] = c
  190. elif t in ["Drill"]:
  191. result["Dimensions"][-1]["Root"]["Drill"] = c
  192. elif t in ["Levels"]:
  193. current = c
  194. level_ids.append(c["ID"])
  195. result["Dimensions"][-1]["Levels"].append(c)
  196. elif t in ["Category"]:
  197. if c["Levels"] in level_ids[0:2]:
  198. result["Dimensions"][-1]["Categories"].append(c)
  199. elif t in ["SpecialCategory"]:
  200. result["Dimensions"][-1]["SpecialCategories"].append(c)
  201. elif t in ["Measure"]:
  202. current = c
  203. result["Measures"].append(c)
  204. elif t in ["Associations"]:
  205. c["Parent"] = current["ID"]
  206. current["Associations"].append(c)
  207. for ds in result["DataSources"]:
  208. for col in ds["Columns"]:
  209. if col["Column"] == c["AssociationReferenced"]:
  210. col["Associations"].append(c)
  211. elif t in ["Signon"]:
  212. result["Signons"].append(c)
  213. elif t in ["Cube"]:
  214. result["Cubes"].append(c)
  215. elif t in ["CustomView"]:
  216. result["CustomViews"].append(c)
  217. elif t in ["CustomViewChildList"]:
  218. for cv in result["CustomViews"]:
  219. if cv["ID"] == c["ID"]:
  220. cv["ChildList"] = c
  221. elif t in ["SecurityNameSpace"]:
  222. result["Security"].append(c)
  223. elif t in ["SecurityObject"]:
  224. result["Security"][-1]["Objects"].append(c)
  225. # else:
  226. # print(t, c)
  227. return result
  228. def build_query(datasource):
  229. table = datasource["Name"]
  230. suffix = "_fm" if datasource["SourceType"] == "CognosSourceQuery" else "_imr"
  231. table_name = f"[staging].[{table}{suffix}]"
  232. view_name = f"[load].[{table}]"
  233. columns = ",\n ".join([extract_column(c) for c in datasource["Columns"]])
  234. return f"CREATE VIEW {view_name}\nAS\nSELECT {columns} \nFROM {table_name}\nGO\n\n"
  235. def extract_column(col):
  236. name = col["Name"]
  237. if "]." in name:
  238. name = name.split("].")[-1]
  239. alias = col["Column"]
  240. is_used = "" if len(col["Associations"]) > 0 else "--"
  241. return f'{is_used}{name} AS "{alias}"'
  242. def convert_file(filename):
  243. with open(filename, "r", encoding="latin-1") as frh:
  244. mdl_str = frh.read()
  245. mdl_str = prepare_mdl_str(mdl_str)
  246. mdl_blocks = mdl_str.split("\n\n")
  247. converted = [convert_block(b) for b in mdl_blocks]
  248. grouped = group_mdl_blocks(converted)
  249. with open(filename[:-4] + "_ori.json", "w") as fwh:
  250. json.dump(grouped, fwh, indent=2)
  251. # yaml.safe_dump(result, open(filename[:-4] + ".yaml", "w"))
  252. without_ids = remove_ids(grouped)
  253. with open(filename[:-4] + ".json", "w") as fwh:
  254. json.dump(without_ids, fwh, indent=2)
  255. queries = [build_query(ds) for ds in grouped["DataSources"]]
  256. with open(filename[:-4] + "_queries.txt", "w", encoding="latin-1") as fwh:
  257. fwh.writelines(queries)
  258. cat_name_to_label = dict(
  259. [
  260. (d["Name"] + "//" + c["Name"], c.get("Label", c.get("SourceValue", "")))
  261. for d in grouped["Dimensions"]
  262. for c in d["Categories"]
  263. ]
  264. )
  265. filename_ids = filename[:-4] + "_ids.json"
  266. if len(grouped["Cubes"]):
  267. cube_name = Path(grouped["Cubes"][0]["MdcFile"]).name
  268. filename_ids = str(Path(filename).parent / cube_name[:-4]) + "_ids.json"
  269. with open(filename_ids, "w") as fwh:
  270. json.dump(cat_name_to_label, fwh, indent=2)
  271. def convert_folder(base_dir):
  272. files = sorted([(f.stat().st_mtime, f) for f in Path(base_dir).rglob("*.mdl")])
  273. for _, filename in files:
  274. convert_file(str(filename))
  275. if __name__ == "__main__":
  276. # convert_file("data/S_Offene_Auftraege.mdl")
  277. # convert_file("data/F_Belege_SKR_SKR_Boettche.mdl")
  278. convert_folder("data/mdl/")