c11_api.py 25 KB

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