c11_api.py 26 KB

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