def test_confluence_attach_file_2(self): credentials = None try: with open(self.secret_file) as json_file: credentials = json.load(json_file) except Exception as err: self.fail("[{0}]: {1}".format(self.secret_file, err)) confluence = Confluence( url=credentials["host"], username=credentials["username"], password=credentials["password"], ) # individual configuration space = "SAN" title = "atlassian-python-rest-api-wrapper" # TODO: check if page are exists fd, filename = tempfile.mkstemp("w") os.write(fd, b"Hello World - Version 1") name = os.path.basename(tempfile.mktemp()) + ".txt" # upload a new file result = confluence.attach_file( filename, name, content_type="text/plain", title=title, space=space, comment="upload from unittest", ) # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ... self.assertTrue("results" in result) self.assertFalse("statusCode" in result) # upload a new version of an existing file os.lseek(fd, 0, 0) os.write(fd, b"Hello Universe - Version 2") result = confluence.attach_file( filename, name, content_type="text/plain", title=title, space=space, comment="upload from unittest", ) # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ... self.assertTrue("id" in result) self.assertFalse("statusCode" in result) os.close(fd) os.remove(filename)
def test_confluence_attach_file_2(self): credentials = None try: with open(self.secret_file) as json_file: credentials = json.load(json_file) except Exception as err: self.fail('[{0}]: {1}'.format(self.secret_file, err)) confluence = Confluence(url=credentials['host'], username=credentials['username'], password=credentials['password']) # individual configuration space = 'SAN' title = 'atlassian-python-rest-api-wrapper' # TODO: check if page are exists fd, filename = tempfile.mkstemp('w') os.write(fd, b'Hello World - Version 1') name = os.path.basename(tempfile.mktemp()) + ".txt" # upload a new file result = confluence.attach_file(filename, name, content_type='text/plain', title=title, space=space, comment='upload from unittest') # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ... self.assertTrue('results' in result) self.assertFalse('statusCode' in result) # upload a new version of an existing file os.lseek(fd, 0, 0) os.write(fd, b'Hello Universe - Version 2') result = confluence.attach_file(filename, name, content_type='text/plain', title=title, space=space, comment='upload from unittest') # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ... self.assertTrue('id' in result) self.assertFalse('statusCode' in result) os.close(fd) os.remove(filename)
def run() -> None: """Main program loop""" args = parse_args() dm = DataModel(args.url, args.http_username, args.http_password, args.uid) dm.load() if dm.status != 200: print("{} Status code {}".format(str(datetime.datetime.now()), dm.status)) return try: old_dm = pickle.load(open('cache.dat', 'rb')) if old_dm == dm: return except FileNotFoundError: print("First run") print("{} Graphing changes".format(str(datetime.datetime.now()))) pickle.dump(dm, open('cache.dat', 'wb')) dot = graphviz.Digraph(comment='Double edge facts') # for obj in dm.objects: # dot.node(obj, obj) for fact in dm.facts: name, s, d, di = fact if name == 'mentions': continue if not d: continue dot.node(s, s) dot.node(d, d) dot.edge(s, d, label=name) if di: dot.edge(s, d, label=name) dot.render('output/double', format='png', renderer='cairo') dot = graphviz.Digraph(comment='Single edge facts') for fact in dm.facts: name, s, d, di = fact if d: continue dot.node(name, label=name, shape='diamond') dot.node(s, s) dot.edge(name, s) dot.render('output/single', format='png', renderer='cairo') dot = graphviz.Digraph(comment='All Double edge facts') for obj in dm.objects: dot.node(obj, obj) for fact in dm.facts: name, s, d, di = fact if not d: continue dot.node(s, s) dot.node(d, d) dot.edge(s, d, label=name) if di and not s == d: dot.edge(s, d, label=name) dot.render('output/complete', format='png', renderer='cairo') if args.parent_id: os.environ.pop('https_proxy') os.environ.pop('http_proxy') confluence = Confluence(url=args.confluence_url, username=args.confluence_user, password=args.confluence_password) confluence.attach_file('output/double.cairo.png', page_id=args.parent_id, title='Double Edged Facts') confluence.attach_file('output/single.cairo.png', page_id=args.parent_id, title='Single Edged Facts') confluence.attach_file('output/complete.cairo.png', page_id=args.parent_id, title='Single Edged Facts')
# coding=utf-8 import logging from datetime import datetime from atlassian import Confluence confluence = Confluence( url="http://localhost:8090", username="******", password="******", ) logging.basicConfig(level=logging.DEBUG) filename = "test_file.txt" with open(filename, "w") as f: f.write(str(datetime.utcnow())) confluence.attach_file(filename, page_id="123456789") link = """<p> <ac:link> <ri:attachment ri:filename="{}"/> </ac:link> </p>""".format(filename) confluence.append_page(123456789, "Page Title", link)
def run() -> None: """Main program loop""" args = parse_args() dm = DataModel(args.url, args.http_username, args.http_password, args.uid, args.cacert) dm.load() if dm.status != 200: print("{} Status code {}".format(str(datetime.datetime.now()), dm.status)) return try: old_dm = pickle.load(open("cache.dat", "rb")) if old_dm == dm: return except FileNotFoundError: print("First run") print("{} Graphing changes".format(str(datetime.datetime.now()))) pickle.dump(dm, open("cache.dat", "wb")) dot = graphviz.Digraph(comment="Double edge facts") # for obj in dm.objects: # dot.node(obj, obj) for fact in dm.facts: name, s, d, di = fact if name == "mentions": continue if not d: continue dot.node(s, s) dot.node(d, d) if di: dot.edge(s, d, label=name, dir="both") else: dot.edge(s, d, label=name) if args.dump_source: with open(os.path.join(args.dump_source, "double.dot"), "w") as f: f.write(dot.source) dot.render("output/double", format="png", renderer="cairo") dot = graphviz.Digraph(comment="Single edge facts") for fact in dm.facts: name, s, d, di = fact if d: continue dot.node(name, label=name, shape="diamond") dot.node(s, s) dot.edge(s, name) if args.dump_source: with open(os.path.join(args.dump_source, "single.dot"), "w") as f: f.write(dot.source) dot.render("output/single", format="png", renderer="cairo") dot = graphviz.Digraph(comment="All Double edge facts") for obj in dm.objects: dot.node(obj, obj) for fact in dm.facts: name, s, d, di = fact if not d: continue dot.node(s, s) dot.node(d, d) if di: dot.edge(s, d, label=name, dir="both") else: dot.edge(s, d, label=name) if args.dump_source: with open(os.path.join(args.dump_source, "complete.dot"), "w") as f: f.write(dot.source) dot.render("output/complete", format="png", renderer="cairo") if args.parent_id: try: os.environ.pop("https_proxy") except KeyError: pass try: os.environ.pop("http_proxy") except KeyError: pass verify_ssl = False if args.cacert else True confluence = Confluence( url=args.confluence_url, username=args.confluence_user, password=args.confluence_password, verify_ssl=verify_ssl, ) confluence.attach_file( "output/double.cairo.png", page_id=args.parent_id, title="Double Edged Facts", ) confluence.attach_file( "output/single.cairo.png", page_id=args.parent_id, title="Single Edged Facts", ) confluence.attach_file( "output/complete.cairo.png", page_id=args.parent_id, title="Single Edged Facts", ) if args.dump_source: confluence.attach_file( os.path.join(args.dump_source, "complete.dot"), page_id=args.parent_id, title="complete source", ) confluence.attach_file( os.path.join(args.dump_source, "double.dot"), page_id=args.parent_id, title="double source", ) confluence.attach_file( os.path.join(args.dump_source, "single.dot"), page_id=args.parent_id, title="single source", )
class ConfluencePublisher(): def __init__( self, url, username, apiToken, pageTitlePrefix, markdownDir, dbPath, space, parentPageId, forceUpdate=False, forceDelete=False, skipUpdate=False, ): self.api = Confluence(url=url, username=username, password=apiToken) self.pageTitlePrefix = pageTitlePrefix self.markdownDir = markdownDir self.kv = KeyValue(dbPath) self.space = space self.parentPageId = parentPageId self.forceUpdate = forceUpdate self.forceDelete = forceDelete self.skipUpdate = skipUpdate self.confluenceRenderer = ConfluenceRenderer(url) self.metadataPlugin = MetadataPlugin() self.renderer = mistune.create_markdown( renderer=self.confluenceRenderer, plugins=[ 'strikethrough', 'footnotes', 'table', 'url', self.metadataPlugin.plugin_metadata ]) # Hack to allow metadata plugin to work (See mistune/block_parser.py) self.renderer.block.rules.remove('thematic_break') def __getFileContent(self, filepath): file = open(filepath, mode='r') content = file.read() file.close() return content def __updatePage(self, space, parentId, filepath, autoindex=False): if autoindex: markdown = '' else: markdown = self.__getFileContent(filepath) metadata = self.kv.load(filepath) currentTitle = metadata['title'] currentHash = metadata['sha256'] hash = self.kv.sha256(markdown) # --- Render (BEGIN) self.metadataPlugin.stack['title'] = None if autoindex: body = self.confluenceRenderer.generate_autoindex() else: body = self.renderer(markdown) if self.metadataPlugin.stack['title'] is None: if autoindex: title = 'Folder ' + os.path.basename(os.path.dirname(filepath)) else: title = os.path.basename(filepath) else: title = self.metadataPlugin.stack['title'] title = self.pageTitlePrefix + title # >>> Removed: + " [" + self.kv.sha256(filepath)[-6:] + "]" # --- Render (END) if currentTitle and currentTitle != title: print('REN => Title: ' + title) pageId = self.api.get_page_id(space, currentTitle) self.api.update_page(pageId, title, body) if currentHash != hash or self.forceUpdate: if autoindex: print('IDX => Title: ' + title) else: print('UPD => Title: ' + title) if self.api.update_or_create(parent_id=parentId, title=title, body=body, representation='storage'): id = self.api.get_page_id(space, title) self.kv.save(filepath, { 'id': id, 'title': title, 'sha256': hash }) return id else: return None else: print('SKP => Title: ' + title) return self.api.get_page_id(space, title) def __deleteAttachment(self, filepath): metadata = self.kv.load(filepath) filename = os.path.basename(filepath) if metadata['id']: try: print('DEL Att. => Title: ' + filename) # https://confluence.atlassian.com/confkb/confluence-rest-api-lacks-delete-method-for-attachments-715361922.html # self.api.delete_attachment_by_id(metadata['id'], 1) self.api.remove_content(metadata['id']) except (HTTPError, ApiError): pass def __updateAttachment(self, space, pageId, filepath): filename = os.path.basename(filepath) print('UPD Att. => Title: ' + filename) results = self.api.attach_file(filepath, name=filename, page_id=pageId, space=space) id = results['id'] if 'id' in results else results['results'][0]['id'] self.kv.save(filepath, {'id': id, 'title': filename, 'sha256': None}) return id def __publishRecursive(self, space, parentId, path): # File: _index.md indexParentId = parentId indexPath = path + os.sep + '_index.md' if os.path.isfile(indexPath): # Use local _index.md file indexParentId = self.__updatePage(space, parentId, indexPath) else: # Autoindex simulate _index.md in Confluence if missing locally # Except for (root) parentPageId because missing in markdownDir! if parentId != self.parentPageId: indexParentId = self.__updatePage(space, parentId, indexPath, True) # Directories: */ for f in os.scandir(path): if f.is_dir(): self.__publishRecursive(space, indexParentId, f.path) # Files: *.* (Except _index.md) for f in os.scandir(path): if f.is_file(): if f.path.endswith(".md"): if not f.path.endswith(os.sep + '_index.md'): self.__updatePage(space, indexParentId, f.path) else: self.__deleteAttachment(f.path) self.__updateAttachment(space, indexParentId, f.path) def delete(self): for filepath in sorted(self.kv.keys()): metadata = self.kv.load(filepath) # Page has Sub-pages (Childs)? indexWithChilds = False if filepath.endswith('_index.md'): childs = 0 if os.path.isdir(os.path.dirname(filepath)): for f in os.scandir(os.path.dirname(filepath)): if f.path.endswith(".md") and \ not f.path.endswith('_index.md'): childs = childs + 1 indexWithChilds = childs > 0 if self.forceDelete \ or (not os.path.isfile(filepath) and not indexWithChilds): print('DEL => Id: ' + metadata['id'] + ', Title: ' + metadata['title']) if filepath.endswith(".md"): try: if self.api.get_page_by_id(metadata['id']): self.api.remove_page(metadata['id']) except HTTPError as ex: code = ex.response.status_code if code != 404: print("DEL Pag. (Error):" + str(code)) else: pass else: self.__deleteAttachment(filepath) self.kv.remove(filepath) def publish(self): self.__publishRecursive(self.space, self.parentPageId, self.markdownDir)
class ConfluencePublisher(): def __init__( self, url, username, api_token, page_title_prefix, markdown_dir, db_path, space, parent_pageid, force_update=False, force_delete=False, skip_update=False, verbose=False): self.api = Confluence(url=url, username=username, password=api_token) self.page_title_prefix = page_title_prefix self.markdown_dir = markdown_dir self.kv = KeyValue(db_path) self.space = space self.parent_pageid = parent_pageid self.force_update = force_update self.force_delete = force_delete self.skip_update = skip_update self.confluence_renderer = ConfluenceRenderer(verbose) self.renderer = mistune.create_markdown( renderer=self.confluence_renderer, plugins=[ plugin_front_matter, DirectiveInclude(), HugoRefLinkPlugin(self.markdown_dir), 'strikethrough', 'footnotes', 'table', 'url', Admonition(), plugin_html_comment, ] ) def __update_page(self, space, parentid, filepath, autoindex=False): metadata = self.kv.load(filepath) current_title = metadata['title'] current_hash = metadata['sha256'] sha_hash = get_file_sha256(filepath) # --- Render (BEGIN) body = '' state = {'front_matter': {}} if autoindex: body = generate_autoindex() state['front_matter']['title'] = \ os.path.basename(os.path.dirname(filepath)).title() else: if filepath.endswith("_index.md"): body = generate_autoindex() body += self.renderer.read(filepath, state) title = '{}{}'.format(self.page_title_prefix, state['front_matter']['title']) # --- Render (END) if current_title and current_title != title: print('REN => Title: ' + title) confluence_page_id = self.api.get_page_id(space, current_title) self.api.update_page(confluence_page_id, title, body) if current_hash != sha_hash or self.force_update: if autoindex: print('IDX => Title: ' + title) else: print('UPD => Title: ' + title) if self.api.update_or_create( parent_id=parentid, title=title, body=body, representation='storage' ): confluence_page_id = self.api.get_page_id(space, title) self.kv.save(filepath, {'id': confluence_page_id, 'title': title, 'sha256': sha_hash}) return confluence_page_id return None else: print('SKP => Title: ' + title) return self.api.get_page_id(space, title) def __delete_attachment(self, filepath): metadata = self.kv.load(filepath) filename = os.path.basename(filepath) if metadata['id']: try: print('DEL Att. => Title: ' + filename) # https://confluence.atlassian.com/confkb/confluence-rest-api-lacks-delete-method-for-attachments-715361922.html # self.api.delete_attachment_by_id(metadata['id'], 1) self.api.remove_content(metadata['id']) except (HTTPError, ApiError): pass def __update_attachment(self, space, pageid, filepath): filename = os.path.basename(filepath) print('UPD Att. => Title: ' + filename) results = self.api.attach_file(filepath, name=filename, page_id=pageid, space=space) confluence_page_id = results['id'] if 'id' in results else results['results'][0]['id'] self.kv.save(filepath, {'id': confluence_page_id, 'title': filename, 'sha256': None}) return confluence_page_id def __publish_recursive(self, space, parentid, path, root=False): # File: _index.md index_parentid = parentid index_path = path + os.sep + '_index.md' if not root: if os.path.isfile(index_path): # Use local _index.md file index_parentid = self.__update_page( space, parentid, index_path) else: # Autoindex simulate _index.md in Confluence if missing locally index_parentid = self.__update_page( space, parentid, index_path, True) # Directories: */ for f in os.scandir(path): if f.is_dir(): self.__publish_recursive(space, index_parentid, f.path) # Files: *.* (Except _index.md) for f in os.scandir(path): if f.is_file(): if f.path.endswith(".md"): if not f.path.endswith(os.sep + '_index.md'): self.__update_page(space, index_parentid, f.path) else: self.__delete_attachment(f.path) self.__update_attachment(space, index_parentid, f.path) def delete(self): for filepath in sorted(self.kv.keys()): metadata = self.kv.load(filepath) # Page has Sub-pages (Childs)? index_with_childs = False if filepath.endswith('_index.md'): childs = 0 if os.path.isdir(os.path.dirname(filepath)): for f in os.scandir(os.path.dirname(filepath)): if f.path.endswith(".md") and \ not f.path.endswith('_index.md'): childs = childs + 1 index_with_childs = childs > 0 if self.force_delete \ or (not os.path.isfile(filepath) and not index_with_childs): print('DEL => Id: ' + metadata['id'] + ', Title: ' + metadata['title']) if filepath.endswith(".md"): try: if self.api.get_page_by_id(metadata['id']): self.api.remove_page(metadata['id']) except HTTPError as ex: code = ex.response.status_code if code != 404: print("DEL Pag. (Error):" + str(code)) else: pass else: self.__delete_attachment(filepath) self.kv.remove(filepath) def publish(self): self.__publish_recursive( self.space, self.parent_pageid, self.markdown_dir, root=True)
class ConfluenceRenderer(MarkdownRenderer): """Render report to comfluence via the atlassian API """ def __init__(self, space="", url="", username="", token="", parent=None): self.parent = parent self.space = space # if not all([space, url, username, token]): # raise ConfluenceAPIError("Must assigne a space, url, unername and token") # Define atlassian api connection self.conn = Confluence(url=url, username=username, password=token, cloud=True) def set_parent(self, parent): """Set id of parent to write conluence page to """ self.parent = parent def set_space(self, space): """Set id of space to write conluence page to """ self.space = space def save(self, report): """Save report to Confluence """ report, plots_to_upload = self._process_images(report) content = self._convert_to_jira_wiki(report) response = self.conn.create_page(self.space, report.title, content, type="page", representation="wiki", parent_id=self.parent) page_id = response['id'] # upload images for filepath, name in plots_to_upload: self._upload_image(filepath, name, page_id) def _process_images(self, report): """Prepare report and images to be uploaded """ out_report = deepcopy(report) out_report.components = [] images_to_upload = [] for component in report.get_components(): if isinstance(component, Plot): # prepare upload filepath = component.get_path() name = os.path.basename(filepath) images_to_upload.append((filepath, name)) # Change Plot to match new_plot = deepcopy(component) new_plot.set_path(name) out_report.add_component(new_plot) else: out_report.add_component(component) return out_report, images_to_upload def _upload_image(self, filepath, name, page_id): """Upload Iamges to Confluence """ self.conn.attach_file(filepath, space=self.space, page_id=page_id, name=name) def _test_connection(self): """Test initialized confluence connection """ def _test_space_page_exists(self): """Test initialized confluence connection """ def _convert_to_jira_wiki(self, report): """Convert Markdown content to Atlassian wiki markup """ content = self._render_markdown(report.get_components()) content = pypandoc.convert_text(content, "jira", "markdown") return content