mdl_convert.py 9.9 KB

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