c11_api.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. import base64
  2. import csv
  3. import json
  4. import logging
  5. import os
  6. import re
  7. import jinja2
  8. import requests
  9. from bs4 import BeautifulSoup
  10. from requests_toolbelt.multipart import decoder
  11. import config
  12. from cognos11.xml_prettify import prettify_xml
  13. def convert_filename(filename: str) -> str:
  14. # Entferne ungültige Zeichen
  15. filename = re.sub(r'[<>"/|?*]', "", filename)
  16. filename = filename.replace("\u2013", "") # Grad-Zeichen
  17. return re.sub(r"[^\x00-\x7F]äöüÄÖÜß", "", filename)
  18. class c11_api:
  19. webservice = ""
  20. templates_dir = ""
  21. # templates_dir = "C:/GlobalCube/Tasks/gctools/templates"
  22. headers = {}
  23. caf = ""
  24. cam = ""
  25. reports = []
  26. folders = []
  27. jobs = []
  28. server_version = "202004"
  29. def __init__(self, cfg: config.Config):
  30. self.cfg = cfg
  31. self.webservice = cfg.cognos11.webservice
  32. self.templates_dir = cfg.tasks_dir + "/scripts/templates"
  33. self._env = jinja2.Environment(
  34. loader=jinja2.FileSystemLoader(self.templates_dir),
  35. autoescape=jinja2.select_autoescape(["html", "xml"]),
  36. )
  37. self.templates = {
  38. "get_report": self._env.get_template("get_report.xml"),
  39. "create_report": self._env.get_template("create_report.xml"),
  40. "update_report": self._env.get_template("update_report.xml"),
  41. "get_package": self._env.get_template("get_package.xml"),
  42. }
  43. @staticmethod
  44. def generate_token(message_base64):
  45. version = "V1".encode("utf-8")
  46. header_len = 4
  47. msg = base64.b64decode(message_base64)[1:]
  48. chunks = []
  49. while len(msg) >= header_len:
  50. chunk_len = int.from_bytes(msg[:header_len], byteorder="little")
  51. msg = msg[header_len:]
  52. chunks.append(msg[:chunk_len])
  53. msg = msg[chunk_len:]
  54. return base64.b64encode(version + chunks[-1]).decode("utf-8")
  55. def login(self):
  56. cred = self.cfg.cognos11.credentials
  57. credentials = {
  58. "parameters": [
  59. {"name": "h_CAM_action", "value": "logonAs"},
  60. {"name": "CAMNamespace", "value": cred.namespace},
  61. {"name": "CAMUsername", "value": cred.username},
  62. {"name": "CAMPassword", "value": cred.password},
  63. ]
  64. }
  65. self.session = requests.Session()
  66. r = self.session.get(self.webservice)
  67. self.headers = {
  68. "Content-Type": "application/json; charset=UTF-8",
  69. "X-XSRF-TOKEN": self.session.cookies.get("XSRF-TOKEN"),
  70. }
  71. r = self.session.post(
  72. self.webservice + "v1/login",
  73. data=json.dumps(credentials),
  74. headers=self.headers,
  75. )
  76. if r.ok:
  77. self.caf = r.json()["cafContextId"]
  78. self.cam = self.generate_token(r.cookies["usersessionid"])
  79. else:
  80. print("!! Error: Not connected to cognos server !!")
  81. print(f"Response: {r.status_code} {r.reason}")
  82. exit(1)
  83. self.server_version = self.get_server_version()
  84. return self
  85. def get_server_version(self):
  86. r = self.session.get(self.webservice + "pat/rsstartupblock_3.js")
  87. res = re.search(r"\=\"(\d{6})\";", r.text)
  88. if res:
  89. return res.group(1)
  90. return "202004"
  91. def get_folders(self):
  92. if len(self.folders) == 0:
  93. self.folders.append({"id": "_dot_public_folders", "name": "Team Content"})
  94. self.load_folder_list()
  95. self.save_config()
  96. return self.folders
  97. def save_config(self):
  98. os.makedirs(self.cfg.cognos11.config_dir, exist_ok=True)
  99. with open(self.cfg.cognos11.folders_file, "w") as fwh:
  100. json.dump(self.folders, fwh, indent=2)
  101. with open(self.cfg.cognos11.reports_file, "w") as fwh:
  102. json.dump(self.reports, fwh, indent=2)
  103. with open(self.cfg.cognos11.jobs_file, "w") as fwh:
  104. json.dump(self.jobs, fwh, indent=2)
  105. def load_config_from_files(self):
  106. with open(self.cfg.cognos11.folders_file, "r") as frh:
  107. self.folders = json.load(frh)
  108. with open(self.cfg.cognos11.reports_file, "r") as frh:
  109. self.reports = json.load(frh)
  110. with open(self.cfg.cognos11.jobs_file, "w") as frh:
  111. self.jobs = json.load(frh)
  112. def load_folder_list(self, folder_id="_dot_public_folders", prefix="Team Content"):
  113. fields = ",".join(
  114. [
  115. "id",
  116. "defaultName",
  117. "defaultDescription",
  118. "type",
  119. "modificationTime",
  120. "target.id",
  121. "target.searchPath",
  122. "target.defaultName",
  123. "base.id",
  124. "base.searchPath",
  125. "base.defaultName",
  126. "parameters",
  127. ]
  128. )
  129. res = self.session.get(
  130. f"{self.webservice}v1/objects/{folder_id}/items?fields={fields}",
  131. headers=self.headers,
  132. )
  133. folder_list = sorted(res.json()["data"], key=lambda x: x["defaultName"])
  134. for f in folder_list:
  135. if f["type"] == "folder":
  136. folder = {
  137. "id": f["id"],
  138. "name": prefix + "/" + f["defaultName"].replace("/", "-"),
  139. }
  140. self.folders.append(folder)
  141. self.load_folder_list(folder["id"], folder["name"])
  142. elif f["type"] in ("report", "reportView", "shortcut"):
  143. report = {
  144. "id": f["id"],
  145. "name": f["defaultName"].replace("/", "-"),
  146. "description": f["defaultDescription"],
  147. "modified": f["modificationTime"],
  148. "path": prefix,
  149. "type": f["type"],
  150. }
  151. if f["type"] == "shortcut":
  152. report["target_id"] = f["target"][0]["id"]
  153. if f["type"] == "reportView":
  154. report["target_id"] = "" if f["base"] is None else f["base"][0]["id"]
  155. report["parameters"] = {}
  156. if f["parameters"] is not None:
  157. params = [p for p in f["parameters"] if len(p["value"]) > 0]
  158. for p in params:
  159. report["parameters"][p["name"]] = dict([(v["use"], v["display"]) for v in p["value"]])
  160. self.reports.append(report)
  161. elif f["type"] == "jobDefinition":
  162. job = {"id": f["id"], "name": f["defaultName"], "path": prefix}
  163. job["details"] = self.get_job_details(job["id"])
  164. self.jobs.append(job)
  165. def get_report_details(self, object_id):
  166. fields = ",".join(
  167. [
  168. "defaultDescription",
  169. "options",
  170. "executionPrompt",
  171. "parameters",
  172. "module.defaultName",
  173. "module.ancestors",
  174. "allowNotification",
  175. "base.id",
  176. "base.searchPath",
  177. "base.defaultName",
  178. "base.defaultDescription",
  179. "base.ancestors",
  180. "base.metadataModelPackage",
  181. "base.module",
  182. ]
  183. )
  184. res = self.session.get(
  185. f"{self.webservice}v1/objects/{object_id}?fields={fields}",
  186. headers=self.headers,
  187. )
  188. res = res.json()["data"][0]
  189. res.pop("_meta", None)
  190. res.pop("id", None)
  191. res.pop("type", None)
  192. res.pop("defaultName", None)
  193. return res
  194. def get_job_details(self, object_id):
  195. fields = ",".join(
  196. [
  197. "userInterfaces",
  198. "disabled",
  199. "runInAdvancedViewer",
  200. "modificationTime",
  201. "canBurst",
  202. "defaultPortalAction",
  203. "base.defaultName",
  204. "tags",
  205. "target.searchPath",
  206. "target.disabled",
  207. "options",
  208. "base.options",
  209. ]
  210. )
  211. res = self.session.get(
  212. f"{self.webservice}v1/objects/{object_id}?fields={fields}",
  213. headers=self.headers,
  214. )
  215. job = res.json()["data"][0]
  216. job.pop("_meta", None)
  217. job.pop("id", None)
  218. job.pop("type", None)
  219. job.pop("defaultName", None)
  220. fields2 = ",".join(
  221. [
  222. r"id,displaySequence,stepObject{defaultName}",
  223. r"stepObject{id},stepObject{parameters}",
  224. r"stepObject{canBurst},options,parameters",
  225. ]
  226. )
  227. res = self.session.get(
  228. f"{self.webservice}v1/objects/{object_id}/items?types=jobStepDefinition&fields={fields2}",
  229. headers=self.headers,
  230. )
  231. steps = res.json()["data"]
  232. for s in steps:
  233. s.pop("_meta", None)
  234. if s["stepObject"] is not None:
  235. s["report_id"] = s["stepObject"][0]["id"]
  236. s.pop("stepObject", None)
  237. job["steps"] = steps
  238. return job
  239. def get_report(self, report_id):
  240. self.get_folders()
  241. report = [r for r in self.reports if r["id"] == report_id]
  242. if len(report) == 0:
  243. return None
  244. report = report[0]
  245. if "meta" not in report:
  246. report = self.get_report_specs(report)
  247. return report
  248. def get_reports_in_folder(self, folder, recursive=False, specs=False):
  249. self.get_folders()
  250. if recursive:
  251. res = [r for r in self.reports if r["path"].startswith(folder)]
  252. else:
  253. res = [r for r in self.reports if r["path"] == folder]
  254. if specs:
  255. for _ in range(5):
  256. res = [self.get_report_specs(r) for r in res]
  257. return res
  258. def get_report_specs(self, report):
  259. if "spec" in report:
  260. return report
  261. report = self.get_report_filename(report)
  262. headers = {
  263. "Content-Type": "text/xml; charset=UTF-8",
  264. "X-XSRF-TOKEN": self.headers["X-XSRF-TOKEN"],
  265. "X-RsCMStoreID": report["id"],
  266. "X-UseRsConsumerMode": "true",
  267. "SOAPAction": f"http://www.ibm.com/xmlns/prod/cognos/reportService/{self.server_version}/",
  268. }
  269. soap = self.templates["get_report"].render(
  270. {
  271. "caf": self.caf,
  272. "cam": self.cam,
  273. "report": report,
  274. "format": "XHTML",
  275. "prompt": "true",
  276. "tracking": "",
  277. "params": {},
  278. }
  279. )
  280. r = self.session.post(self.webservice + "v1/reports", data=soap, headers=headers)
  281. if r.status_code == 500:
  282. bs = BeautifulSoup(r.text, "xml")
  283. report["error"] = bs.find_all("messageString")[0].string
  284. logging.error(f"{report['path']}/{report['name']}")
  285. logging.error(report["error"])
  286. return report
  287. parts = decoder.MultipartDecoder.from_response(r).parts
  288. meta = {"required": {}, "optional": {}}
  289. bs = BeautifulSoup(parts[0].content, "xml")
  290. result = bs.find("bus:result")
  291. details = result.find("bus:primaryRequest")
  292. params = details.find("bus:parameters")
  293. for item in params.find_all("item"):
  294. if item["xsi:type"] != "bus:parameterValue":
  295. continue
  296. k = item.find("bus:name").text
  297. # v = item.find("bus:value").find_all("item")
  298. v = {}
  299. for opt in item.find("bus:value").find_all("item"):
  300. if opt.find("bus:display") is not None:
  301. v[opt.find("bus:use").text] = opt.find("bus:display").text
  302. else:
  303. v[opt.find("bus:use").text] = opt.find("bus:use").text
  304. if len(v.items()) > 0:
  305. meta["required"][k] = v
  306. bs = BeautifulSoup(parts[1].content, "lxml")
  307. for sv in bs.find_all("selectvalue"):
  308. k = sv["parameter"]
  309. v = dict([(opt.get("usevalue", ""), opt.get("displayvalue", "")) for opt in sv.find_all("selectoption")])
  310. meta["optional"][k] = v
  311. for sv in bs.find_all("selectdate"):
  312. k = sv["parameter"]
  313. v = dict([(opt["usevalue"], opt.get("displayvalue", "")) for opt in sv.find_all("selectoption")])
  314. meta["optional"][k] = v
  315. filename = self.cfg.cognos11.config_dir + f"/params/{report['path']}/{report['name']}.json"
  316. os.makedirs(os.path.dirname(filename), exist_ok=True)
  317. json.dump(meta, open(filename, "w"), indent=2)
  318. report["cube"] = self.get_cube_name(meta)
  319. report["meta"] = meta
  320. if len(meta["optional"]) > 0:
  321. report["spec"] = parts[2].text
  322. return report
  323. @staticmethod
  324. def get_cube_name(meta):
  325. for param in meta["optional"].values():
  326. for key in param.keys():
  327. if key.startswith("["):
  328. res = re.search(r"^\[([^\]]*)\]", key)
  329. return res.group(1)
  330. def get_report_filename(self, report):
  331. path = report["path"].replace("Team Content/ReportOutput", "").replace("/", "\\")
  332. report["filename"] = f"{self.cfg.cognos11.reportoutput_dir}\\{path}\\{report['name']}.pdf"
  333. report["format"] = "PDF"
  334. if report["name"][-5:].lower() == ".xlsx":
  335. report["format"] = "spreadsheetML"
  336. report["filename"] = report["filename"][:-4]
  337. report["params"] = list(re.findall(r"\[([^\]]+)\]", report["filename"]))
  338. for i, p in enumerate(report["params"]):
  339. report["filename"] = report["filename"].replace("[" + p + "]", "{" + str(i) + "}")
  340. report["filename"] = convert_filename(report["filename"])
  341. return report
  342. def request_unstubbed(self, report_id):
  343. report = self.get_report(report_id)
  344. if "spec" not in report:
  345. return ""
  346. payload = json.dumps({"reportspec_stubbed": report["spec"], "storeid": report["id"]})
  347. headers = {
  348. "Content-Type": "application/json; charset=UTF-8",
  349. "X-XSRF-TOKEN": self.session.cookies.get("XSRF-TOKEN"),
  350. "authenticityToken": self.cam,
  351. "X-UseRsConsumerMode": "true",
  352. "cafContextId": self.caf,
  353. }
  354. r = self.session.post(self.webservice + "v1/reports/unstubreport", data=payload, headers=headers)
  355. if r.status_code != 200:
  356. logging.error(f"{report['path']}/{report['name']}")
  357. logging.error(r.text)
  358. return
  359. unstubbed = json.loads(r.text)["reportspec_full"]
  360. unstubbed = re.sub(r' iid="[^"]*"', "", unstubbed)
  361. bs = BeautifulSoup(unstubbed, "xml")
  362. for xa in bs.find_all("XMLAttributes"):
  363. if (
  364. xa.find_all("XMLAttribute", {"name": "RS_dataType"})
  365. or xa.find_all("XMLAttribute", {"name": "RS_CreateExtendedDataItems"})
  366. or xa.find_all("XMLAttribute", {"name": "RS_legacyDrillDown"})
  367. ):
  368. continue
  369. if xa.find_all("XMLAttribute", {"name": "supportsDefaultDataFormatting"}):
  370. for xa2 in xa.find_all("XMLAttribute"):
  371. if xa2.attrs["name"] != "supportsDefaultDataFormatting":
  372. xa2.decompose()
  373. continue
  374. xa.decompose()
  375. for cti in bs.find_all("crosstabIntersection"):
  376. if len(list(cti.children)) == 0:
  377. cti.decompose()
  378. unstubbed_report = str(bs)
  379. unstubbed_report = prettify_xml(unstubbed_report).replace("'", "&quot;")
  380. return unstubbed_report
  381. def get_report_headers(self, report_id=None):
  382. res = {
  383. "Content-Type": "text/xml; charset=UTF-8",
  384. "X-XSRF-TOKEN": self.headers["X-XSRF-TOKEN"],
  385. "X-UseRsConsumerMode": "true",
  386. "SOAPAction": f"http://www.ibm.com/xmlns/prod/cognos/reportService/{self.server_version}/",
  387. }
  388. if report_id is not None:
  389. res["X-RsCMStoreID"] = report_id
  390. return res
  391. def request_file(self, report_id, params, format="PDF"):
  392. report = self.get_report(report_id)
  393. headers = self.get_report_headers(report_id)
  394. soap = (
  395. self.templates["get_report"]
  396. .render(
  397. {
  398. "caf": self.caf,
  399. "cam": self.cam,
  400. "report": report,
  401. "format": format,
  402. "prompt": "false",
  403. "tracking": "",
  404. "params": params,
  405. }
  406. )
  407. .encode("utf-8")
  408. )
  409. r = self.session.post(self.webservice + "v1/reports", data=soap, headers=headers)
  410. bs = BeautifulSoup(r.text, "xml")
  411. if r.status_code == 200:
  412. try:
  413. parts = decoder.MultipartDecoder.from_response(r).parts
  414. except decoder.NonMultipartContentTypeException:
  415. return 500, "Timeout"
  416. return 200, parts[1].content
  417. error = bs.find_all("messageString")[0].string
  418. logging.info(error)
  419. return r.status_code, error
  420. def get_users(self, export_dir):
  421. self.get_licenses(export_dir)
  422. ns = self.get_namespaces()
  423. for item in ns:
  424. temp_items = self.get_namespace_items(item["id"])
  425. item["items"] = self.get_sub_items(temp_items)
  426. with open(export_dir + "/namespaces.json", "w") as fwh:
  427. json.dump(ns, fwh, indent=2)
  428. return ns
  429. def get_sub_items(self, items):
  430. for sub_item in items:
  431. if sub_item["type"] == "namespaceFolder":
  432. temp_items = self.get_namespace_folder_items(sub_item["id"])
  433. sub_item["items"] = self.get_sub_items(temp_items)
  434. if sub_item["type"] == "role":
  435. sub_item["members"] = self.get_role_members(sub_item["id"])
  436. if sub_item["type"] == "group":
  437. sub_item["members"] = self.get_group_members(sub_item["id"])
  438. return items
  439. def show_users(self, ns, indent=0):
  440. indent_str = " " * indent
  441. style = {
  442. "account": "@",
  443. "namespace": "#",
  444. "role": "~",
  445. "group": "+",
  446. "namespaceFolder": "*",
  447. }
  448. for item in ns:
  449. if item["type"] == "account":
  450. print(indent_str + f"@ {item['defaultName']} (uid={item.get('userName', 'xx')})")
  451. else:
  452. print(indent_str + f"{style.get(item['type'], '*')} {item['defaultName']} ({item['type']})")
  453. if "members" in item:
  454. for user in item["members"].get("users", []):
  455. print(indent_str + f" -> @ {user['defaultName']} (uid={user['userName']})")
  456. for group in item["members"].get("groups", []):
  457. print(indent_str + f" -> + {group['defaultName']} ({group['type']})")
  458. for role in item["members"].get("roles", []):
  459. print(indent_str + f" -> ~ {role['defaultName']} ({role['type']})")
  460. if "items" in item:
  461. self.show_users(item["items"], indent + 2)
  462. def export_users(self, ns, export_dir: str):
  463. data = self.export_users_recursive(ns, [])
  464. if len(data) > 0:
  465. with open(export_dir + "\\cognos_users.csv", "w", encoding="latin-1", newline="") as fwh:
  466. csv_fwh = csv.DictWriter(fwh, fieldnames=data[0].keys(), delimiter=";")
  467. csv_fwh.writeheader()
  468. csv_fwh.writerows(data)
  469. def export_users_recursive(self, tree, prev_folder_list):
  470. res = []
  471. for item in tree:
  472. folder_list = prev_folder_list + [item["defaultName"]]
  473. if "items" in item:
  474. res.extend(self.export_users_recursive(item["items"], folder_list))
  475. elif "members" in item and isinstance(item["members"], dict):
  476. for e in ["users", "groups", "roles"]:
  477. res.extend(self.export_users_recursive(item["members"].get(e, []), folder_list))
  478. else:
  479. res.append(
  480. {
  481. "Name": item["defaultName"],
  482. "Typ": item["type"],
  483. "Pfad": "/".join(prev_folder_list),
  484. "ID": item["id"],
  485. "Suchpfad": item["searchPath"].replace('"', "'"),
  486. "UID": item.get("userName", ""),
  487. "Email": item.get("email", ""),
  488. "Erstellung": item.get("creationTime", ""),
  489. }
  490. )
  491. return res
  492. def get_licenses(self, export_dir):
  493. res = self.session.get(
  494. f"{self.webservice}v1/licenses/details",
  495. headers=self.headers,
  496. )
  497. data = res.text.replace(",", ";")
  498. with open(export_dir + "/licenses.csv", "w", encoding="latin-1", newline="") as fwh:
  499. fwh.write(data)
  500. return data
  501. def get_namespaces(self):
  502. fields = ",".join(
  503. [
  504. "id",
  505. "defaultName",
  506. "searchPath",
  507. "objectClass",
  508. "creationTime",
  509. "modificationTime",
  510. "type",
  511. ]
  512. )
  513. res = self.session.get(
  514. f"{self.webservice}v1/namespaces?fields={fields}",
  515. headers=self.headers,
  516. )
  517. return res.json()["data"]
  518. def get_namespace_items(self, id):
  519. fields = ",".join(
  520. [
  521. "id",
  522. "defaultName",
  523. "searchPath",
  524. "objectClass",
  525. "creationTime",
  526. "modificationTime",
  527. "type",
  528. "email",
  529. "givenName",
  530. "surname",
  531. "userName",
  532. ]
  533. )
  534. res = self.session.get(
  535. f"{self.webservice}v1/namespaces/{id}/items?fields={fields}",
  536. headers=self.headers,
  537. )
  538. return res.json()["data"]
  539. def get_namespace_folder_items(self, id):
  540. fields = ",".join(
  541. [
  542. "id",
  543. "defaultName",
  544. "searchPath",
  545. "objectClass",
  546. "creationTime",
  547. "modificationTime",
  548. "type",
  549. ]
  550. )
  551. res = self.session.get(
  552. f"{self.webservice}v1/folders/{id}/items?fields={fields}",
  553. headers=self.headers,
  554. )
  555. return res.json()["data"]
  556. def get_role_members(self, id):
  557. res = self.session.get(
  558. f"{self.webservice}v1/roles/{id}/members",
  559. headers=self.headers,
  560. )
  561. return res.json()
  562. def get_group_members(self, id):
  563. res = self.session.get(
  564. f"{self.webservice}v1/groups/{id}/members",
  565. headers=self.headers,
  566. )
  567. return res.json()
  568. def create_report(self, folder_id, fullpath):
  569. # self.session.get(self.webservice + 'v1/reports/templates?path=%2Fcontent%2Ffolder%5B%40name%3D%27Templates%27%5D/
  570. # *[@objectClass=%27interactiveReport%27%20or%20@objectClass=%27report%27%20or%20@objectClass=%27reportTemplate%27]&maxResults=100&locale=de')
  571. # self.session.get(self.webservice + 'v1/reports/startupconfig?keys=supportedContentLocales,supportedCurrencies,
  572. # supportedFonts,metadataInformationURI,glossaryURI&locale=de')
  573. headers = self.get_report_headers()
  574. soap = self.templates["get_package"].render({"caf": self.caf, "cam": self.cam}).encode("utf-8")
  575. r = self.session.post(self.webservice + "v1/reports", data=soap, headers=headers)
  576. open("package.xml", "wb").write(r.content)
  577. search_path = self.request_search_path(folder_id)
  578. report_name = os.path.basename(fullpath)
  579. unstubbed = open(fullpath, "rb").read()
  580. headers["SOAPAction"] = f"http://www.ibm.com/xmlns/prod/cognos/reportService/{self.server_version}/.session"
  581. headers["Referer"] = "http://localhost:9300/bi/pat/rsapp.htm"
  582. # headers['caf'] = self.caf
  583. soap = (
  584. self.templates["create_report"]
  585. .render(
  586. {
  587. "caf": self.caf,
  588. "cam": self.cam,
  589. "search_path": search_path,
  590. "report_name": report_name,
  591. "unstubbed": unstubbed,
  592. }
  593. )
  594. .encode("utf-8")
  595. )
  596. r = self.session.post(self.webservice + "v1/reports", data=soap, headers=headers)
  597. open("request_create.xml", "wb").write(r.request.body)
  598. print(r.status_code)
  599. print(r.text)
  600. def update_report(self, report, fullpath):
  601. search_path = self.request_search_path(report["id"])
  602. unstubbed = open(fullpath, "r").read()
  603. headers = self.get_report_headers(report["id"])
  604. # headers['Referer'] = 'http://localhost:9300/bi/pat/rsapp.htm'
  605. headers["caf"] = self.caf
  606. soap = self.templates["update_report"].render(
  607. {
  608. "caf": self.caf,
  609. "cam": self.cam,
  610. "search_path": search_path,
  611. "unstubbed": unstubbed,
  612. }
  613. ) # .encode("utf-8")
  614. r = self.session.post(self.webservice + "v1/reports", data=soap, headers=headers)
  615. # open('request_update.xml', 'wb').write(r.request.body)
  616. print(r.status_code)
  617. print(r.text)
  618. def create_folder(self, parent_id, folder_name):
  619. data = json.dumps({"defaultName": folder_name, "type": "folder"})
  620. res = self.session.post(
  621. f"{self.webservice}v1/objects/{parent_id}/items",
  622. headers=self.headers,
  623. data=data,
  624. )
  625. if res.status_code == 201:
  626. loc = res.headers.get("Location")
  627. folder_id = loc.split("/")[-1]
  628. self.folders.append({"id": folder_id, "name": folder_name})
  629. return folder_id
  630. def request_search_path(self, id):
  631. res = self.session.get(f"{self.webservice}v1/objects/{id}?fields=searchPath", headers=self.headers)
  632. return res.json()["data"][0]["searchPath"]